Browse Source

Add more stuff for the min-juce module

tags/1.9.8
falkTX 7 years ago
parent
commit
b80049b3c6
31 changed files with 9502 additions and 52 deletions
  1. +3
    -0
      .gitignore
  2. +1
    -1
      source/modules/juce_audio_graph/buffers/juce_AudioSampleBuffer.h
  3. +373
    -0
      source/modules/juce_audio_graph/containers/juce_LinkedListPointer.h
  4. +856
    -0
      source/modules/juce_audio_graph/containers/juce_ReferenceCountedArray.h
  5. +469
    -0
      source/modules/juce_audio_graph/containers/juce_SortedSet.h
  6. +1210
    -0
      source/modules/juce_audio_graph/files/juce_File.cpp
  7. +987
    -0
      source/modules/juce_audio_graph/files/juce_File.h
  8. +119
    -0
      source/modules/juce_audio_graph/files/juce_TemporaryFile.cpp
  9. +171
    -0
      source/modules/juce_audio_graph/files/juce_TemporaryFile.h
  10. +3
    -30
      source/modules/juce_audio_graph/juce_audio_graph.cpp
  11. +30
    -19
      source/modules/juce_audio_graph/juce_audio_graph.h
  12. +24
    -0
      source/modules/juce_audio_graph/main.cpp
  13. +423
    -0
      source/modules/juce_audio_graph/memory/juce_ReferenceCountedObject.h
  14. +85
    -0
      source/modules/juce_audio_graph/misc/juce_Result.cpp
  15. +127
    -0
      source/modules/juce_audio_graph/misc/juce_Result.h
  16. +0
    -2
      source/modules/juce_audio_graph/processors/juce_AudioProcessorGraph.h
  17. +58
    -0
      source/modules/juce_audio_graph/streams/juce_FileInputSource.cpp
  18. +68
    -0
      source/modules/juce_audio_graph/streams/juce_FileInputSource.h
  19. +86
    -0
      source/modules/juce_audio_graph/streams/juce_FileInputStream.cpp
  20. +98
    -0
      source/modules/juce_audio_graph/streams/juce_FileInputStream.h
  21. +80
    -0
      source/modules/juce_audio_graph/streams/juce_InputSource.h
  22. +79
    -0
      source/modules/juce_audio_graph/text/juce_Identifier.cpp
  23. +142
    -0
      source/modules/juce_audio_graph/text/juce_Identifier.h
  24. +508
    -0
      source/modules/juce_audio_graph/text/juce_StringArray.cpp
  25. +441
    -0
      source/modules/juce_audio_graph/text/juce_StringArray.h
  26. +168
    -0
      source/modules/juce_audio_graph/text/juce_StringPool.cpp
  27. +98
    -0
      source/modules/juce_audio_graph/text/juce_StringPool.h
  28. +895
    -0
      source/modules/juce_audio_graph/xml/juce_XmlDocument.cpp
  29. +183
    -0
      source/modules/juce_audio_graph/xml/juce_XmlDocument.h
  30. +942
    -0
      source/modules/juce_audio_graph/xml/juce_XmlElement.cpp
  31. +775
    -0
      source/modules/juce_audio_graph/xml/juce_XmlElement.h

+ 3
- 0
.gitignore View File

@@ -199,3 +199,6 @@ data/macos/*.*/
data/macos/Carla/ data/macos/Carla/


data/windows/python/ data/windows/python/

source/modules/juce_audio_graph/test
source/modules/juce_audio_graph/test.d

+ 1
- 1
source/modules/juce_audio_graph/buffers/juce_AudioSampleBuffer.h View File

@@ -569,7 +569,7 @@ public:
{ {
float* const d = channels [channel] + startSample; float* const d = channels [channel] + startSample;
if (gain == 0.0f)
if (carla_isZero (gain))
carla_zeroFloats (d, numSamples); carla_zeroFloats (d, numSamples);
else else
carla_multiply (d, gain, numSamples); carla_multiply (d, gain, numSamples);


+ 373
- 0
source/modules/juce_audio_graph/containers/juce_LinkedListPointer.h View File

@@ -0,0 +1,373 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_LINKEDLISTPOINTER_H_INCLUDED
#define JUCE_LINKEDLISTPOINTER_H_INCLUDED
//==============================================================================
/**
Helps to manipulate singly-linked lists of objects.
For objects that are designed to contain a pointer to the subsequent item in the
list, this class contains methods to deal with the list. To use it, the ObjectType
class that it points to must contain a LinkedListPointer called nextListItem, e.g.
@code
struct MyObject
{
int x, y, z;
// A linkable object must contain a member with this name and type, which must be
// accessible by the LinkedListPointer class. (This doesn't mean it has to be public -
// you could make your class a friend of a LinkedListPointer<MyObject> instead).
LinkedListPointer<MyObject> nextListItem;
};
LinkedListPointer<MyObject> myList;
myList.append (new MyObject());
myList.append (new MyObject());
int numItems = myList.size(); // returns 2
MyObject* lastInList = myList.getLast();
@endcode
*/
template <class ObjectType>
class LinkedListPointer
{
public:
//==============================================================================
/** Creates a null pointer to an empty list. */
LinkedListPointer() noexcept
: item (nullptr)
{
}
/** Creates a pointer to a list whose head is the item provided. */
explicit LinkedListPointer (ObjectType* const headItem) noexcept
: item (headItem)
{
}
/** Sets this pointer to point to a new list. */
LinkedListPointer& operator= (ObjectType* const newItem) noexcept
{
item = newItem;
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
LinkedListPointer (LinkedListPointer&& other) noexcept
: item (other.item)
{
other.item = nullptr;
}
LinkedListPointer& operator= (LinkedListPointer&& other) noexcept
{
jassert (this != &other); // hopefully the compiler should make this situation impossible!
item = other.item;
other.item = nullptr;
return *this;
}
#endif
//==============================================================================
/** Returns the item which this pointer points to. */
inline operator ObjectType*() const noexcept
{
return item;
}
/** Returns the item which this pointer points to. */
inline ObjectType* get() const noexcept
{
return item;
}
/** Returns the last item in the list which this pointer points to.
This will iterate the list and return the last item found. Obviously the speed
of this operation will be proportional to the size of the list. If the list is
empty the return value will be this object.
If you're planning on appending a number of items to your list, it's much more
efficient to use the Appender class than to repeatedly call getLast() to find the end.
*/
LinkedListPointer& getLast() noexcept
{
LinkedListPointer* l = this;
while (l->item != nullptr)
l = &(l->item->nextListItem);
return *l;
}
/** Returns the number of items in the list.
Obviously with a simple linked list, getting the size involves iterating the list, so
this can be a lengthy operation - be careful when using this method in your code.
*/
int size() const noexcept
{
int total = 0;
for (ObjectType* i = item; i != nullptr; i = i->nextListItem)
++total;
return total;
}
/** Returns the item at a given index in the list.
Since the only way to find an item is to iterate the list, this operation can obviously
be slow, depending on its size, so you should be careful when using this in algorithms.
*/
LinkedListPointer& operator[] (int index) noexcept
{
LinkedListPointer* l = this;
while (--index >= 0 && l->item != nullptr)
l = &(l->item->nextListItem);
return *l;
}
/** Returns the item at a given index in the list.
Since the only way to find an item is to iterate the list, this operation can obviously
be slow, depending on its size, so you should be careful when using this in algorithms.
*/
const LinkedListPointer& operator[] (int index) const noexcept
{
const LinkedListPointer* l = this;
while (--index >= 0 && l->item != nullptr)
l = &(l->item->nextListItem);
return *l;
}
/** Returns true if the list contains the given item. */
bool contains (const ObjectType* const itemToLookFor) const noexcept
{
for (ObjectType* i = item; i != nullptr; i = i->nextListItem)
if (itemToLookFor == i)
return true;
return false;
}
//==============================================================================
/** Inserts an item into the list, placing it before the item that this pointer
currently points to.
*/
void insertNext (ObjectType* const newItem)
{
jassert (newItem != nullptr);
jassert (newItem->nextListItem == nullptr);
newItem->nextListItem = item;
item = newItem;
}
/** Inserts an item at a numeric index in the list.
Obviously this will involve iterating the list to find the item at the given index,
so be careful about the impact this may have on execution time.
*/
void insertAtIndex (int index, ObjectType* newItem)
{
jassert (newItem != nullptr);
LinkedListPointer* l = this;
while (index != 0 && l->item != nullptr)
{
l = &(l->item->nextListItem);
--index;
}
l->insertNext (newItem);
}
/** Replaces the object that this pointer points to, appending the rest of the list to
the new object, and returning the old one.
*/
ObjectType* replaceNext (ObjectType* const newItem) noexcept
{
jassert (newItem != nullptr);
jassert (newItem->nextListItem == nullptr);
ObjectType* const oldItem = item;
item = newItem;
item->nextListItem = oldItem->nextListItem.item;
oldItem->nextListItem.item = nullptr;
return oldItem;
}
/** Adds an item to the end of the list.
This operation involves iterating the whole list, so can be slow - if you need to
append a number of items to your list, it's much more efficient to use the Appender
class than to repeatedly call append().
*/
void append (ObjectType* const newItem)
{
getLast().item = newItem;
}
/** Creates copies of all the items in another list and adds them to this one.
This will use the ObjectType's copy constructor to try to create copies of each
item in the other list, and appends them to this list.
*/
void addCopyOfList (const LinkedListPointer& other)
{
LinkedListPointer* insertPoint = this;
for (ObjectType* i = other.item; i != nullptr; i = i->nextListItem)
{
insertPoint->insertNext (new ObjectType (*i));
insertPoint = &(insertPoint->item->nextListItem);
}
}
/** Removes the head item from the list.
This won't delete the object that is removed, but returns it, so the caller can
delete it if necessary.
*/
ObjectType* removeNext() noexcept
{
ObjectType* const oldItem = item;
if (oldItem != nullptr)
{
item = oldItem->nextListItem;
oldItem->nextListItem.item = nullptr;
}
return oldItem;
}
/** Removes a specific item from the list.
Note that this will not delete the item, it simply unlinks it from the list.
*/
void remove (ObjectType* const itemToRemove)
{
if (LinkedListPointer* const l = findPointerTo (itemToRemove))
l->removeNext();
}
/** Iterates the list, calling the delete operator on all of its elements and
leaving this pointer empty.
*/
void deleteAll()
{
while (item != nullptr)
{
ObjectType* const oldItem = item;
item = oldItem->nextListItem;
delete oldItem;
}
}
/** Finds a pointer to a given item.
If the item is found in the list, this returns the pointer that points to it. If
the item isn't found, this returns null.
*/
LinkedListPointer* findPointerTo (ObjectType* const itemToLookFor) noexcept
{
LinkedListPointer* l = this;
while (l->item != nullptr)
{
if (l->item == itemToLookFor)
return l;
l = &(l->item->nextListItem);
}
return nullptr;
}
/** Copies the items in the list to an array.
The destArray must contain enough elements to hold the entire list - no checks are
made for this!
*/
void copyToArray (ObjectType** destArray) const noexcept
{
jassert (destArray != nullptr);
for (ObjectType* i = item; i != nullptr; i = i->nextListItem)
*destArray++ = i;
}
/** Swaps this pointer with another one */
void swapWith (LinkedListPointer& other) noexcept
{
std::swap (item, other.item);
}
//==============================================================================
/**
Allows efficient repeated insertions into a list.
You can create an Appender object which points to the last element in your
list, and then repeatedly call Appender::append() to add items to the end
of the list in O(1) time.
*/
class Appender
{
public:
/** Creates an appender which will add items to the given list.
*/
Appender (LinkedListPointer& endOfListPointer) noexcept
: endOfList (&endOfListPointer)
{
// This can only be used to add to the end of a list.
jassert (endOfListPointer.item == nullptr);
}
/** Appends an item to the list. */
void append (ObjectType* const newItem) noexcept
{
*endOfList = newItem;
endOfList = &(newItem->nextListItem);
}
private:
LinkedListPointer* endOfList;
JUCE_DECLARE_NON_COPYABLE (Appender)
};
private:
//==============================================================================
ObjectType* item;
JUCE_DECLARE_NON_COPYABLE (LinkedListPointer)
};
#endif // JUCE_LINKEDLISTPOINTER_H_INCLUDED

+ 856
- 0
source/modules/juce_audio_graph/containers/juce_ReferenceCountedArray.h View File

@@ -0,0 +1,856 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED
#define JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED
//==============================================================================
/**
Holds a list of objects derived from ReferenceCountedObject, or which implement basic
reference-count handling methods.
The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave.
A ReferenceCountedArray holds objects derived from ReferenceCountedObject,
and takes care of incrementing and decrementing their ref counts when they
are added and removed from the array.
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
@see Array, OwnedArray, StringArray
*/
template <class ObjectClass>
class ReferenceCountedArray
{
public:
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr;
//==============================================================================
/** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray
*/
ReferenceCountedArray() noexcept
: numUsed (0)
{
}
/** Creates a copy of another array */
ReferenceCountedArray (const ReferenceCountedArray& other) noexcept
{
numUsed = other.size();
data.setAllocatedSize (numUsed);
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
for (int i = numUsed; --i >= 0;)
if (ObjectClass* o = data.elements[i])
o->incReferenceCount();
}
/** Creates a copy of another array */
template <class OtherObjectClass>
ReferenceCountedArray (const ReferenceCountedArray<OtherObjectClass>& other) noexcept
{
numUsed = other.size();
data.setAllocatedSize (numUsed);
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
for (int i = numUsed; --i >= 0;)
if (ObjectClass* o = data.elements[i])
o->incReferenceCount();
}
/** Copies another array into this one.
Any existing objects in this array will first be released.
*/
ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept
{
ReferenceCountedArray otherCopy (other);
swapWith (otherCopy);
return *this;
}
/** Copies another array into this one.
Any existing objects in this array will first be released.
*/
template <class OtherObjectClass>
ReferenceCountedArray<ObjectClass>& operator= (const ReferenceCountedArray<OtherObjectClass>& other) noexcept
{
ReferenceCountedArray<ObjectClass> otherCopy (other);
swapWith (otherCopy);
return *this;
}
/** Destructor.
Any objects in the array will be released, and may be deleted if not referenced from elsewhere.
*/
~ReferenceCountedArray()
{
releaseAllObjects();
}
//==============================================================================
/** Removes all objects from the array.
Any objects in the array that whose reference counts drop to zero will be deleted.
*/
void clear()
{
releaseAllObjects();
data.setAllocatedSize (0);
}
/** Removes all objects from the array without freeing the array's allocated storage.
Any objects in the array that whose reference counts drop to zero will be deleted.
@see clear
*/
void clearQuick()
{
releaseAllObjects();
}
/** Returns the current number of objects in the array. */
inline int size() const noexcept
{
return numUsed;
}
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return size() == 0;
}
/** Returns a pointer to the object at this index in the array.
If the index is out-of-range, this will return a null pointer, (and
it could be null anyway, because it's ok for the array to hold null
pointers as well as objects).
@see getUnchecked
*/
inline ObjectClassPtr operator[] (const int index) const noexcept
{
return getObjectPointer (index);
}
/** Returns a pointer to the object at this index in the array, without checking
whether the index is in-range.
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
it can be used when you're sure the index is always going to be legal.
*/
inline ObjectClassPtr getUnchecked (const int index) const noexcept
{
return getObjectPointerUnchecked (index);
}
/** Returns a raw pointer to the object at this index in the array.
If the index is out-of-range, this will return a null pointer, (and
it could be null anyway, because it's ok for the array to hold null
pointers as well as objects).
@see getUnchecked
*/
inline ObjectClass* getObjectPointer (const int index) const noexcept
{
if (isPositiveAndBelow (index, numUsed))
{
jassert (data.elements != nullptr);
return data.elements [index];
}
return ObjectClassPtr();
}
/** Returns a raw pointer to the object at this index in the array, without checking
whether the index is in-range.
*/
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept
{
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
return data.elements [index];
}
/** Returns a pointer to the first object in the array.
This will return a null pointer if the array's empty.
@see getLast
*/
inline ObjectClassPtr getFirst() const noexcept
{
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements [0];
}
return ObjectClassPtr();
}
/** Returns a pointer to the last object in the array.
This will return a null pointer if the array's empty.
@see getFirst
*/
inline ObjectClassPtr getLast() const noexcept
{
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements [numUsed - 1];
}
return ObjectClassPtr();
}
/** Returns a pointer to the actual array data.
This pointer will only be valid until the next time a non-const method
is called on the array.
*/
inline ObjectClass** getRawDataPointer() const noexcept
{
return data.elements;
}
//==============================================================================
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** begin() const noexcept
{
return data.elements;
}
/** Returns a pointer to the element which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** end() const noexcept
{
return data.elements + numUsed;
}
//==============================================================================
/** Finds the index of the first occurrence of an object in the array.
@param objectToLookFor the object to look for
@returns the index at which the object was found, or -1 if it's not found
*/
int indexOf (const ObjectClass* const objectToLookFor) const noexcept
{
ObjectClass** e = data.elements.getData();
ObjectClass** const endPointer = e + numUsed;
while (e != endPointer)
{
if (objectToLookFor == *e)
return static_cast<int> (e - data.elements.getData());
++e;
}
return -1;
}
/** Returns true if the array contains a specified object.
@param objectToLookFor the object to look for
@returns true if the object is in the array
*/
bool contains (const ObjectClass* const objectToLookFor) const noexcept
{
ObjectClass** e = data.elements.getData();
ObjectClass** const endPointer = e + numUsed;
while (e != endPointer)
{
if (objectToLookFor == *e)
return true;
++e;
}
return false;
}
/** Appends a new object to the end of the array.
This will increase the new object's reference count.
@param newObject the new object to add to the array
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
*/
ObjectClass* add (ObjectClass* const newObject) noexcept
{
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
data.elements [numUsed++] = newObject;
if (newObject != nullptr)
newObject->incReferenceCount();
return newObject;
}
/** Inserts a new object into the array at the given index.
If the index is less than 0 or greater than the size of the array, the
element will be added to the end of the array.
Otherwise, it will be inserted into the array, moving all the later elements
along to make room.
This will increase the new object's reference count.
@param indexToInsertAt the index at which the new element should be inserted
@param newObject the new object to add to the array
@see add, addSorted, addIfNotAlreadyThere, set
*/
ObjectClass* insert (int indexToInsertAt,
ObjectClass* const newObject) noexcept
{
if (indexToInsertAt < 0)
return add (newObject);
if (indexToInsertAt > numUsed)
indexToInsertAt = numUsed;
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
ObjectClass** const e = data.elements + indexToInsertAt;
const int numToMove = numUsed - indexToInsertAt;
if (numToMove > 0)
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
*e = newObject;
if (newObject != nullptr)
newObject->incReferenceCount();
++numUsed;
return newObject;
}
/** Appends a new object at the end of the array as long as the array doesn't
already contain it.
If the array already contains a matching object, nothing will be done.
@param newObject the new object to add to the array
@returns true if the object has been added, false otherwise
*/
bool addIfNotAlreadyThere (ObjectClass* const newObject) noexcept
{
if (contains (newObject))
return false;
add (newObject);
return true;
}
/** Replaces an object in the array with a different one.
If the index is less than zero, this method does nothing.
If the index is beyond the end of the array, the new object is added to the end of the array.
The object being added has its reference count increased, and if it's replacing
another object, then that one has its reference count decreased, and may be deleted.
@param indexToChange the index whose value you want to change
@param newObject the new value to set for this index.
@see add, insert, remove
*/
void set (const int indexToChange,
ObjectClass* const newObject)
{
if (indexToChange >= 0)
{
if (newObject != nullptr)
newObject->incReferenceCount();
if (indexToChange < numUsed)
{
if (ObjectClass* o = data.elements [indexToChange])
releaseObject (o);
data.elements [indexToChange] = newObject;
}
else
{
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
data.elements [numUsed++] = newObject;
}
}
}
/** Adds elements from another array to the end of this array.
@param arrayToAddFrom the array from which to copy the elements
@param startIndex the first element of the other array to start copying from
@param numElementsToAdd how many elements to add from the other array. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
void addArray (const ReferenceCountedArray<ObjectClass>& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1) noexcept
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
if (numElementsToAdd > 0)
{
data.ensureAllocatedSize (numUsed + numElementsToAdd);
while (--numElementsToAdd >= 0)
add (arrayToAddFrom.getUnchecked (startIndex++));
}
}
/** Inserts a new object into the array assuming that the array is sorted.
This will use a comparator to find the position at which the new object
should go. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator object to use to compare the elements - see the
sort() method for details about this object's form
@param newObject the new object to insert to the array
@returns the index at which the new object was added
@see add, sort
*/
template <class ElementComparator>
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
{
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
insert (index, newObject);
return index;
}
/** Inserts or replaces an object in the array, assuming it is sorted.
This is similar to addSorted, but if a matching element already exists, then it will be
replaced by the new one, rather than the new one being added as well.
*/
template <class ElementComparator>
void addOrReplaceSorted (ElementComparator& comparator,
ObjectClass* newObject) noexcept
{
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
if (index > 0 && comparator.compareElements (newObject, data.elements [index - 1]) == 0)
set (index - 1, newObject); // replace an existing object that matches
else
insert (index, newObject); // no match, so insert the new one
}
/** Finds the index of an object in the array, assuming that the array is sorted.
This will use a comparator to do a binary-chop to find the index of the given
element, if it exists. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator to use to compare the elements - see the sort()
method for details about the form this object should take
@param objectToLookFor the object to search for
@returns the index of the element, or -1 if it's not found
@see addSorted, sort
*/
template <class ElementComparator>
int indexOfSorted (ElementComparator& comparator,
const ObjectClass* const objectToLookFor) const noexcept
{
ignoreUnused (comparator);
int s = 0, e = numUsed;
while (s < e)
{
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0)
return s;
const int halfway = (s + e) / 2;
if (halfway == s)
break;
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0)
s = halfway;
else
e = halfway;
}
return -1;
}
//==============================================================================
/** Removes an object from the array.
This will remove the object at a given index and move back all the
subsequent objects to close the gap.
If the index passed in is out-of-range, nothing will happen.
The object that is removed will have its reference count decreased,
and may be deleted if not referenced from elsewhere.
@param indexToRemove the index of the element to remove
@see removeObject, removeRange
*/
void remove (const int indexToRemove)
{
if (isPositiveAndBelow (indexToRemove, numUsed))
{
ObjectClass** const e = data.elements + indexToRemove;
if (ObjectClass* o = *e)
releaseObject (o);
--numUsed;
const int numberToShift = numUsed - indexToRemove;
if (numberToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
}
/** Removes and returns an object from the array.
This will remove the object at a given index and return it, moving back all
the subsequent objects to close the gap. If the index passed in is out-of-range,
nothing will happen and a null pointer will be returned.
@param indexToRemove the index of the element to remove
@see remove, removeObject, removeRange
*/
ObjectClassPtr removeAndReturn (const int indexToRemove)
{
ObjectClassPtr removedItem;
if (isPositiveAndBelow (indexToRemove, numUsed))
{
ObjectClass** const e = data.elements + indexToRemove;
if (ObjectClass* o = *e)
{
removedItem = o;
releaseObject (o);
}
--numUsed;
const int numberToShift = numUsed - indexToRemove;
if (numberToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
return removedItem;
}
/** Removes the first occurrence of a specified object from the array.
If the item isn't found, no action is taken. If it is found, it is
removed and has its reference count decreased.
@param objectToRemove the object to try to remove
@see remove, removeRange
*/
void removeObject (ObjectClass* const objectToRemove)
{
remove (indexOf (objectToRemove));
}
/** Removes a range of objects from the array.
This will remove a set of objects, starting from the given index,
and move any subsequent elements down to close the gap.
If the range extends beyond the bounds of the array, it will
be safely clipped to the size of the array.
The objects that are removed will have their reference counts decreased,
and may be deleted if not referenced from elsewhere.
@param startIndex the index of the first object to remove
@param numberToRemove how many objects should be removed
@see remove, removeObject
*/
void removeRange (const int startIndex,
const int numberToRemove)
{
const int start = jlimit (0, numUsed, startIndex);
const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
if (endIndex > start)
{
int i;
for (i = start; i < endIndex; ++i)
{
if (ObjectClass* o = data.elements[i])
{
releaseObject (o);
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
}
}
const int rangeSize = endIndex - start;
ObjectClass** e = data.elements + start;
i = numUsed - endIndex;
numUsed -= rangeSize;
while (--i >= 0)
{
*e = e [rangeSize];
++e;
}
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
}
/** Removes the last n objects from the array.
The objects that are removed will have their reference counts decreased,
and may be deleted if not referenced from elsewhere.
@param howManyToRemove how many objects to remove from the end of the array
@see remove, removeObject, removeRange
*/
void removeLast (int howManyToRemove = 1)
{
if (howManyToRemove > numUsed)
howManyToRemove = numUsed;
while (--howManyToRemove >= 0)
remove (numUsed - 1);
}
/** Swaps a pair of objects in the array.
If either of the indexes passed in is out-of-range, nothing will happen,
otherwise the two objects at these positions will be exchanged.
*/
void swap (const int index1,
const int index2) noexcept
{
if (isPositiveAndBelow (index1, numUsed)
&& isPositiveAndBelow (index2, numUsed))
{
std::swap (data.elements [index1],
data.elements [index2]);
}
}
/** Moves one of the objects to a different position.
This will move the object to a specified index, shuffling along
any intervening elements as required.
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
@param currentIndex the index of the object to be moved. If this isn't a
valid index, then nothing will be done
@param newIndex the index at which you'd like this object to end up. If this
is less than zero, it will be moved to the end of the array
*/
void move (const int currentIndex,
int newIndex) noexcept
{
if (currentIndex != newIndex)
{
if (isPositiveAndBelow (currentIndex, numUsed))
{
if (! isPositiveAndBelow (newIndex, numUsed))
newIndex = numUsed - 1;
ObjectClass* const value = data.elements [currentIndex];
if (newIndex > currentIndex)
{
memmove (data.elements + currentIndex,
data.elements + currentIndex + 1,
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
}
else
{
memmove (data.elements + newIndex + 1,
data.elements + newIndex,
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
}
data.elements [newIndex] = value;
}
}
}
//==============================================================================
/** This swaps the contents of this array with those of another array.
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
because it just swaps their internal pointers.
*/
template <class OtherArrayType>
void swapWith (OtherArrayType& otherArray) noexcept
{
data.swapWith (otherArray.data);
std::swap (numUsed, otherArray.numUsed);
}
//==============================================================================
/** Compares this array to another one.
@returns true only if the other array contains the same objects in the same order
*/
bool operator== (const ReferenceCountedArray& other) const noexcept
{
if (numUsed != other.numUsed)
return false;
for (int i = numUsed; --i >= 0;)
if (data.elements [i] != other.data.elements [i])
return false;
return true;
}
/** Compares this array to another one.
@see operator==
*/
bool operator!= (const ReferenceCountedArray<ObjectClass>& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
/** Sorts the elements in the array.
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 (ElementType first, ElementType 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.
@see sortArray
*/
template <class ElementComparator>
void sort (ElementComparator& comparator,
const bool retainOrderOfEquivalentItems = false) const noexcept
{
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems);
}
//==============================================================================
/** Reduces the amount of storage being used by the array.
Arrays typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads() noexcept
{
data.shrinkToNoMoreThan (numUsed);
}
/** Increases the array's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the array won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (const int minNumElements)
{
data.ensureAllocatedSize (minNumElements);
}
private:
//==============================================================================
ArrayAllocationBase <ObjectClass*> data;
int numUsed;
void releaseAllObjects()
{
while (numUsed > 0)
if (ObjectClass* o = data.elements [--numUsed])
releaseObject (o);
jassert (numUsed == 0);
}
static void releaseObject (ObjectClass* o)
{
if (o->decReferenceCountWithoutDeleting())
delete o;
}
};
#endif // JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED

+ 469
- 0
source/modules/juce_audio_graph/containers/juce_SortedSet.h View File

@@ -0,0 +1,469 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_SORTEDSET_H_INCLUDED
#define JUCE_SORTEDSET_H_INCLUDED
//==============================================================================
/**
Holds a set of unique primitive objects, such as ints or doubles.
A set can only hold one item with a given value, so if for example it's a
set of integers, attempting to add the same integer twice will do nothing
the second time.
Internally, the list of items is kept sorted (which means that whatever
kind of primitive type is used must support the ==, <, >, <= and >= operators
to determine the order), and searching the set for known values is very fast
because it uses a binary-chop method.
Note that if you're using a class or struct as the element type, it must be
capable of being copied or moved with a straightforward memcpy, rather than
needing construction and destruction code.
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
@see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
*/
template <class ElementType>
class SortedSet
{
public:
//==============================================================================
/** Creates an empty set. */
SortedSet() noexcept
{
}
/** Creates a copy of another set.
@param other the set to copy
*/
SortedSet (const SortedSet& other)
: data (other.data)
{
}
/** Destructor. */
~SortedSet() noexcept
{
}
/** Copies another set over this one.
@param other the set to copy
*/
SortedSet& operator= (const SortedSet& other) noexcept
{
data = other.data;
return *this;
}
//==============================================================================
/** Compares this set to another one.
Two sets are considered equal if they both contain the same set of elements.
@param other the other set to compare with
*/
bool operator== (const SortedSet<ElementType>& other) const noexcept
{
return data == other.data;
}
/** Compares this set to another one.
Two sets are considered equal if they both contain the same set of elements.
@param other the other set to compare with
*/
bool operator!= (const SortedSet<ElementType>& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
/** Removes all elements from the set.
This will remove all the elements, and free any storage that the set is
using. To clear it without freeing the storage, use the clearQuick()
method instead.
@see clearQuick
*/
void clear() noexcept
{
data.clear();
}
/** Removes all elements from the set without freeing the array's allocated storage.
@see clear
*/
void clearQuick() noexcept
{
data.clearQuick();
}
//==============================================================================
/** Returns the current number of elements in the set. */
inline int size() const noexcept
{
return data.size();
}
/** Returns true if the set is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return size() == 0;
}
/** Returns one of the elements in the set.
If the index passed in is beyond the range of valid elements, this
will return zero.
If you're certain that the index will always be a valid element, you
can call getUnchecked() instead, which is faster.
@param index the index of the element being requested (0 is the first element in the set)
@see getUnchecked, getFirst, getLast
*/
inline ElementType operator[] (const int index) const noexcept
{
return data [index];
}
/** Returns one of the elements in the set, without checking the index passed in.
Unlike the operator[] method, this will try to return an element without
checking that the index is within the bounds of the set, so should only
be used when you're confident that it will always be a valid index.
@param index the index of the element being requested (0 is the first element in the set)
@see operator[], getFirst, getLast
*/
inline ElementType getUnchecked (const int index) const noexcept
{
return data.getUnchecked (index);
}
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
This is like getUnchecked, but returns a direct reference to the element, so that
you can alter it directly. Obviously this can be dangerous, so only use it when
absolutely necessary.
@param index the index of the element being requested (0 is the first element in the array)
*/
inline ElementType& getReference (const int index) const noexcept
{
return data.getReference (index);
}
/** Returns the first element in the set, or 0 if the set is empty.
@see operator[], getUnchecked, getLast
*/
inline ElementType getFirst() const noexcept
{
return data.getFirst();
}
/** Returns the last element in the set, or 0 if the set is empty.
@see operator[], getUnchecked, getFirst
*/
inline ElementType getLast() const noexcept
{
return data.getLast();
}
//==============================================================================
/** Returns a pointer to the first element in the set.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ElementType* begin() const noexcept
{
return data.begin();
}
/** Returns a pointer to the element which follows the last element in the set.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ElementType* end() const noexcept
{
return data.end();
}
//==============================================================================
/** Finds the index of the first element which matches the value passed in.
This will search the set for the given object, and return the index
of its first occurrence. If the object isn't found, the method will return -1.
@param elementToLookFor the value or object to look for
@returns the index of the object, or -1 if it's not found
*/
int indexOf (const ElementType& elementToLookFor) const noexcept
{
int s = 0;
int e = data.size();
for (;;)
{
if (s >= e)
return -1;
if (elementToLookFor == data.getReference (s))
return s;
const int halfway = (s + e) / 2;
if (halfway == s)
return -1;
if (elementToLookFor < data.getReference (halfway))
e = halfway;
else
s = halfway;
}
}
/** Returns true if the set contains at least one occurrence of an object.
@param elementToLookFor the value or object to look for
@returns true if the item is found
*/
bool contains (const ElementType& elementToLookFor) const noexcept
{
return indexOf (elementToLookFor) >= 0;
}
//==============================================================================
/** Adds a new element to the set, (as long as it's not already in there).
Note that if a matching element already exists, the new value will be assigned
to the existing one using operator=, so that if there are any differences between
the objects which were not recognised by the object's operator==, then the
set will always contain a copy of the most recently added one.
@param newElement the new object to add to the set
@returns true if the value was added, or false if it already existed
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
*/
bool add (const ElementType& newElement) noexcept
{
int s = 0;
int e = data.size();
while (s < e)
{
ElementType& elem = data.getReference (s);
if (newElement == elem)
{
elem = newElement; // force an update in case operator== permits differences.
return false;
}
const int halfway = (s + e) / 2;
const bool isBeforeHalfway = (newElement < data.getReference (halfway));
if (halfway == s)
{
if (! isBeforeHalfway)
++s;
break;
}
if (isBeforeHalfway)
e = halfway;
else
s = halfway;
}
data.insert (s, newElement);
return true;
}
/** Adds elements from an array to this set.
@param elementsToAdd the array of elements to add
@param numElementsToAdd how many elements are in this other array
@see add
*/
void addArray (const ElementType* elementsToAdd,
int numElementsToAdd) noexcept
{
while (--numElementsToAdd >= 0)
add (*elementsToAdd++);
}
/** Adds elements from another set to this one.
@param setToAddFrom the set from which to copy the elements
@param startIndex the first element of the other set to start copying from
@param numElementsToAdd how many elements to add from the other set. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
template <class OtherSetType>
void addSet (const OtherSetType& setToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1) noexcept
{
jassert (this != &setToAddFrom);
if (this != &setToAddFrom)
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
numElementsToAdd = setToAddFrom.size() - startIndex;
if (numElementsToAdd > 0)
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
}
}
//==============================================================================
/** Removes an element from the set.
This will remove the element at a given index.
If the index passed in is out-of-range, nothing will happen.
@param indexToRemove the index of the element to remove
@returns the element that has been removed
@see removeValue, removeRange
*/
ElementType remove (const int indexToRemove) noexcept
{
return data.removeAndReturn (indexToRemove);
}
/** Removes an item from the set.
This will remove the given element from the set, if it's there.
@param valueToRemove the object to try to remove
@see remove, removeRange
*/
void removeValue (const ElementType valueToRemove) noexcept
{
data.remove (indexOf (valueToRemove));
}
/** Removes any elements which are also in another set.
@param otherSet the other set in which to look for elements to remove
@see removeValuesNotIn, remove, removeValue, removeRange
*/
template <class OtherSetType>
void removeValuesIn (const OtherSetType& otherSet) noexcept
{
if (this == &otherSet)
{
clear();
}
else if (otherSet.size() > 0)
{
for (int i = data.size(); --i >= 0;)
if (otherSet.contains (data.getReference (i)))
remove (i);
}
}
/** Removes any elements which are not found in another set.
Only elements which occur in this other set will be retained.
@param otherSet the set in which to look for elements NOT to remove
@see removeValuesIn, remove, removeValue, removeRange
*/
template <class OtherSetType>
void removeValuesNotIn (const OtherSetType& otherSet) noexcept
{
if (this != &otherSet)
{
if (otherSet.size() <= 0)
{
clear();
}
else
{
for (int i = data.size(); --i >= 0;)
if (! otherSet.contains (data.getReference (i)))
remove (i);
}
}
}
/** This swaps the contents of this array with those of another array.
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
because it just swaps their internal pointers.
*/
template <class OtherSetType>
void swapWith (OtherSetType& otherSet) noexcept
{
data.swapWith (otherSet.data);
}
//==============================================================================
/** Reduces the amount of storage being used by the set.
Sets typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads() noexcept
{
data.minimiseStorageOverheads();
}
/** Increases the set's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the set won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (const int minNumElements)
{
data.ensureStorageAllocated (minNumElements);
}
private:
//==============================================================================
Array<ElementType> data;
};
#if JUCE_MSVC
#pragma warning (pop)
#endif
#endif // JUCE_SORTEDSET_H_INCLUDED

+ 1210
- 0
source/modules/juce_audio_graph/files/juce_File.cpp
File diff suppressed because it is too large
View File


+ 987
- 0
source/modules/juce_audio_graph/files/juce_File.h View File

@@ -0,0 +1,987 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILE_H_INCLUDED
#define JUCE_FILE_H_INCLUDED
//==============================================================================
/**
Represents a local file or directory.
This class encapsulates the absolute pathname of a file or directory, and
has methods for finding out about the file and changing its properties.
To read or write to the file, there are methods for returning an input or
output stream.
@see FileInputStream, FileOutputStream
*/
class JUCE_API File
{
public:
//==============================================================================
/** Creates an (invalid) file object.
The file is initially set to an empty path, so getFullPathName() will return
an empty string.
You can use its operator= method to point it at a proper file.
*/
File() noexcept {}
/** Creates a file from an absolute path.
If the path supplied is a relative path, it is taken to be relative
to the current working directory (see File::getCurrentWorkingDirectory()),
but this isn't a recommended way of creating a file, because you
never know what the CWD is going to be.
On the Mac/Linux, the path can include "~" notation for referring to
user home directories.
*/
File (const String& absolutePath);
/** Creates a copy of another file object. */
File (const File&);
/** Destructor. */
~File() noexcept {}
/** Sets the file based on an absolute pathname.
If the path supplied is a relative path, it is taken to be relative
to the current working directory (see File::getCurrentWorkingDirectory()),
but this isn't a recommended way of creating a file, because you
never know what the CWD is going to be.
On the Mac/Linux, the path can include "~" notation for referring to
user home directories.
*/
File& operator= (const String& newAbsolutePath);
/** Copies from another file object. */
File& operator= (const File& otherFile);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
File (File&&) noexcept;
File& operator= (File&&) noexcept;
#endif
//==============================================================================
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
/** This static constant is used for referring to an 'invalid' file.
Bear in mind that you should avoid this kind of static variable, and always prefer
to use File() or {} if you need a default-constructed File object.
*/
static const File nonexistent;
#endif
//==============================================================================
/** Checks whether the file actually exists.
@returns true if the file exists, either as a file or a directory.
@see existsAsFile, isDirectory
*/
bool exists() const;
/** Checks whether the file exists and is a file rather than a directory.
@returns true only if this is a real file, false if it's a directory
or doesn't exist
@see exists, isDirectory
*/
bool existsAsFile() const;
/** Checks whether the file is a directory that exists.
@returns true only if the file is a directory which actually exists, so
false if it's a file or doesn't exist at all
@see exists, existsAsFile
*/
bool isDirectory() const;
/** Returns the size of the file in bytes.
@returns the number of bytes in the file, or 0 if it doesn't exist.
*/
int64 getSize() const;
/** Utility function to convert a file size in bytes to a neat string description.
So for example 100 would return "100 bytes", 2000 would return "2 KB",
2000000 would produce "2 MB", etc.
*/
static String descriptionOfSizeInBytes (int64 bytes);
//==============================================================================
/** Returns the complete, absolute path of this file.
This includes the filename and all its parent folders. On Windows it'll
also include the drive letter prefix; on Mac or Linux it'll be a complete
path starting from the root folder.
If you just want the file's name, you should use getFileName() or
getFileNameWithoutExtension().
@see getFileName, getRelativePathFrom
*/
const String& getFullPathName() const noexcept { return fullPath; }
/** Returns the last section of the pathname.
Returns just the final part of the path - e.g. if the whole path
is "/moose/fish/foo.txt" this will return "foo.txt".
For a directory, it returns the final part of the path - e.g. for the
directory "/moose/fish" it'll return "fish".
If the filename begins with a dot, it'll return the whole filename, e.g. for
"/moose/.fish", it'll return ".fish"
@see getFullPathName, getFileNameWithoutExtension
*/
String getFileName() const;
/** Creates a relative path that refers to a file relatively to a given directory.
e.g. File ("/moose/foo.txt").getRelativePathFrom (File ("/moose/fish/haddock"))
would return "../../foo.txt".
If it's not possible to navigate from one file to the other, an absolute
path is returned. If the paths are invalid, an empty string may also be
returned.
@param directoryToBeRelativeTo the directory which the resultant string will
be relative to. If this is actually a file rather than
a directory, its parent directory will be used instead.
If it doesn't exist, it's assumed to be a directory.
@see getChildFile, isAbsolutePath
*/
String getRelativePathFrom (const File& directoryToBeRelativeTo) const;
//==============================================================================
/** Returns the file's extension.
Returns the file extension of this file, also including the dot.
e.g. "/moose/fish/foo.txt" would return ".txt"
@see hasFileExtension, withFileExtension, getFileNameWithoutExtension
*/
String getFileExtension() const;
/** Checks whether the file has a given extension.
@param extensionToTest the extension to look for - it doesn't matter whether or
not this string has a dot at the start, so ".wav" and "wav"
will have the same effect. To compare with multiple extensions, this
parameter can contain multiple strings, separated by semi-colons -
so, for example: hasFileExtension (".jpeg;png;gif") would return
true if the file has any of those three extensions.
@see getFileExtension, withFileExtension, getFileNameWithoutExtension
*/
bool hasFileExtension (StringRef extensionToTest) const;
/** Returns a version of this file with a different file extension.
e.g. File ("/moose/fish/foo.txt").withFileExtension ("html") returns "/moose/fish/foo.html"
@param newExtension the new extension, either with or without a dot at the start (this
doesn't make any difference). To get remove a file's extension altogether,
pass an empty string into this function.
@see getFileName, getFileExtension, hasFileExtension, getFileNameWithoutExtension
*/
File withFileExtension (StringRef newExtension) const;
/** Returns the last part of the filename, without its file extension.
e.g. for "/moose/fish/foo.txt" this will return "foo".
@see getFileName, getFileExtension, hasFileExtension, withFileExtension
*/
String getFileNameWithoutExtension() const;
//==============================================================================
/** Returns a 32-bit hash-code that identifies this file.
This is based on the filename. Obviously it's possible, although unlikely, that
two files will have the same hash-code.
*/
int hashCode() const;
/** Returns a 64-bit hash-code that identifies this file.
This is based on the filename. Obviously it's possible, although unlikely, that
two files will have the same hash-code.
*/
int64 hashCode64() const;
//==============================================================================
/** Returns a file that represents a relative (or absolute) sub-path of the current one.
This will find a child file or directory of the current object.
e.g.
File ("/moose/fish").getChildFile ("foo.txt") will produce "/moose/fish/foo.txt".
File ("/moose/fish").getChildFile ("haddock/foo.txt") will produce "/moose/fish/haddock/foo.txt".
File ("/moose/fish").getChildFile ("../foo.txt") will produce "/moose/foo.txt".
If the string is actually an absolute path, it will be treated as such, e.g.
File ("/moose/fish").getChildFile ("/foo.txt") will produce "/foo.txt"
@see getSiblingFile, getParentDirectory, getRelativePathFrom, isAChildOf
*/
File getChildFile (StringRef relativeOrAbsolutePath) const;
/** Returns a file which is in the same directory as this one.
This is equivalent to getParentDirectory().getChildFile (name).
@see getChildFile, getParentDirectory
*/
File getSiblingFile (StringRef siblingFileName) const;
//==============================================================================
/** Returns the directory that contains this file or directory.
e.g. for "/moose/fish/foo.txt" this will return "/moose/fish".
*/
File getParentDirectory() const;
/** Checks whether a file is somewhere inside a directory.
Returns true if this file is somewhere inside a subdirectory of the directory
that is passed in. Neither file actually has to exist, because the function
just checks the paths for similarities.
e.g. File ("/moose/fish/foo.txt").isAChildOf ("/moose") is true.
File ("/moose/fish/foo.txt").isAChildOf ("/moose/fish") is also true.
*/
bool isAChildOf (const File& potentialParentDirectory) const;
//==============================================================================
/** Chooses a filename relative to this one that doesn't already exist.
If this file is a directory, this will return a child file of this
directory that doesn't exist, by adding numbers to a prefix and suffix until
it finds one that isn't already there.
If the prefix + the suffix doesn't exist, it won't bother adding a number.
e.g. File ("/moose/fish").getNonexistentChildFile ("foo", ".txt", true) might
return "/moose/fish/foo(2).txt" if there's already a file called "foo.txt".
@param prefix the string to use for the filename before the number
@param suffix the string to add to the filename after the number
@param putNumbersInBrackets if true, this will create filenames in the
format "prefix(number)suffix", if false, it will leave the
brackets out.
*/
File getNonexistentChildFile (const String& prefix,
const String& suffix,
bool putNumbersInBrackets = true) const;
/** Chooses a filename for a sibling file to this one that doesn't already exist.
If this file doesn't exist, this will just return itself, otherwise it
will return an appropriate sibling that doesn't exist, e.g. if a file
"/moose/fish/foo.txt" exists, this might return "/moose/fish/foo(2).txt".
@param putNumbersInBrackets whether to add brackets around the numbers that
get appended to the new filename.
*/
File getNonexistentSibling (bool putNumbersInBrackets = true) const;
//==============================================================================
/** Compares the pathnames for two files. */
bool operator== (const File&) const;
/** Compares the pathnames for two files. */
bool operator!= (const File&) const;
/** Compares the pathnames for two files. */
bool operator< (const File&) const;
/** Compares the pathnames for two files. */
bool operator> (const File&) const;
//==============================================================================
/** Checks whether a file can be created or written to.
@returns true if it's possible to create and write to this file. If the file
doesn't already exist, this will check its parent directory to
see if writing is allowed.
@see setReadOnly
*/
bool hasWriteAccess() const;
/** Changes the write-permission of a file or directory.
@param shouldBeReadOnly whether to add or remove write-permission
@param applyRecursively if the file is a directory and this is true, it will
recurse through all the subfolders changing the permissions
of all files
@returns true if it manages to change the file's permissions.
@see hasWriteAccess
*/
bool setReadOnly (bool shouldBeReadOnly,
bool applyRecursively = false) const;
/** Changes the execute-permissions of a file.
@param shouldBeExecutable whether to add or remove execute-permission
@returns true if it manages to change the file's permissions.
*/
bool setExecutePermission (bool shouldBeExecutable) const;
/** Returns true if this file is a hidden or system file.
The criteria for deciding whether a file is hidden are platform-dependent.
*/
bool isHidden() const;
/** Returns a unique identifier for the file, if one is available.
Depending on the OS and file-system, this may be a unix inode number or
a win32 file identifier, or 0 if it fails to find one. The number will
be unique on the filesystem, but not globally.
*/
uint64 getFileIdentifier() const;
/** If possible, this will try to create a version string for the given file.
The OS may be able to look at the file and give a version for it - e.g. with
executables, bundles, dlls, etc. If no version is available, this will
return an empty string.
*/
String getVersion() const;
//==============================================================================
/** Creates an empty file if it doesn't already exist.
If the file that this object refers to doesn't exist, this will create a file
of zero size.
If it already exists or is a directory, this method will do nothing.
If the parent directories of the File do not exist then this method will
recursively create the parent directories.
@returns a result to indicate whether the file was created successfully,
or an error message if it failed.
@see createDirectory
*/
Result create() const;
/** Creates a new directory for this filename.
This will try to create the file as a directory, and will also create
any parent directories it needs in order to complete the operation.
@returns a result to indicate whether the directory was created successfully, or
an error message if it failed.
@see create
*/
Result createDirectory() const;
/** Deletes a file.
If this file is actually a directory, it may not be deleted correctly if it
contains files. See deleteRecursively() as a better way of deleting directories.
@returns true if the file has been successfully deleted (or if it didn't exist to
begin with).
@see deleteRecursively
*/
bool deleteFile() const;
/** Deletes a file or directory and all its subdirectories.
If this file is a directory, this will try to delete it and all its subfolders. If
it's just a file, it will just try to delete the file.
@returns true if the file and all its subfolders have been successfully deleted
(or if it didn't exist to begin with).
@see deleteFile
*/
bool deleteRecursively() const;
/** Moves this file or folder to the trash.
@returns true if the operation succeeded. It could fail if the trash is full, or
if the file is write-protected, so you should check the return value
and act appropriately.
*/
bool moveToTrash() const;
/** Moves or renames a file.
Tries to move a file to a different location.
If the target file already exists, this will attempt to delete it first, and
will fail if this can't be done.
Note that the destination file isn't the directory to put it in, it's the actual
filename that you want the new file to have.
Also note that on some OSes (e.g. Windows), moving files between different
volumes may not be possible.
@returns true if the operation succeeds
*/
bool moveFileTo (const File& targetLocation) const;
/** Copies a file.
Tries to copy a file to a different location.
If the target file already exists, this will attempt to delete it first, and
will fail if this can't be done.
@returns true if the operation succeeds
*/
bool copyFileTo (const File& targetLocation) const;
/** Replaces a file.
Replace the file in the given location, assuming the replaced files identity.
Depending on the file system this will preserve file attributes such as
creation date, short file name, etc.
If replacement succeeds the original file is deleted.
@returns true if the operation succeeds
*/
bool replaceFileIn (const File& targetLocation) const;
/** Copies a directory.
Tries to copy an entire directory, recursively.
If this file isn't a directory or if any target files can't be created, this
will return false.
@param newDirectory the directory that this one should be copied to. Note that this
is the name of the actual directory to create, not the directory
into which the new one should be placed, so there must be enough
write privileges to create it if it doesn't exist. Any files inside
it will be overwritten by similarly named ones that are copied.
*/
bool copyDirectoryTo (const File& newDirectory) const;
//==============================================================================
/** Used in file searching, to specify whether to return files, directories, or both.
*/
enum TypesOfFileToFind
{
findDirectories = 1, /**< Use this flag to indicate that you want to find directories. */
findFiles = 2, /**< Use this flag to indicate that you want to find files. */
findFilesAndDirectories = 3, /**< Use this flag to indicate that you want to find both files and directories. */
ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */
};
/** Searches inside a directory for files matching a wildcard pattern.
Assuming that this file is a directory, this method will search it
for either files or subdirectories whose names match a filename pattern.
@param results an array to which File objects will be added for the
files that the search comes up with
@param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to
return files, directories, or both. If the ignoreHiddenFiles flag
is also added to this value, hidden files won't be returned
@param searchRecursively if true, all subdirectories will be recursed into to do
an exhaustive search
@param wildCardPattern the filename pattern to search for, e.g. "*.txt"
@returns the number of results that have been found
@see getNumberOfChildFiles, DirectoryIterator
*/
int findChildFiles (Array<File>& results,
int whatToLookFor,
bool searchRecursively,
const String& wildCardPattern = "*") const;
/** Searches inside a directory and counts how many files match a wildcard pattern.
Assuming that this file is a directory, this method will search it
for either files or subdirectories whose names match a filename pattern,
and will return the number of matches found.
This isn't a recursive call, and will only search this directory, not
its children.
@param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to
count files, directories, or both. If the ignoreHiddenFiles flag
is also added to this value, hidden files won't be counted
@param wildCardPattern the filename pattern to search for, e.g. "*.txt"
@returns the number of matches found
@see findChildFiles, DirectoryIterator
*/
int getNumberOfChildFiles (int whatToLookFor,
const String& wildCardPattern = "*") const;
/** Returns true if this file is a directory that contains one or more subdirectories.
@see isDirectory, findChildFiles
*/
bool containsSubDirectories() const;
//==============================================================================
/** Creates a stream to read from this file.
@returns a stream that will read from this file (initially positioned at the
start of the file), or nullptr if the file can't be opened for some reason
@see createOutputStream, loadFileAsData
*/
FileInputStream* createInputStream() const;
/** Creates a stream to write to this file.
If the file exists, the stream that is returned will be positioned ready for
writing at the end of the file, so you might want to use deleteFile() first
to write to an empty file.
@returns a stream that will write to this file (initially positioned at the
end of the file), or nullptr if the file can't be opened for some reason
@see createInputStream, appendData, appendText
*/
FileOutputStream* createOutputStream (size_t bufferSize = 0x8000) const;
//==============================================================================
/** Loads a file's contents into memory as a block of binary data.
Of course, trying to load a very large file into memory will blow up, so
it's better to check first.
@param result the data block to which the file's contents should be appended - note
that if the memory block might already contain some data, you
might want to clear it first
@returns true if the file could all be read into memory
*/
bool loadFileAsData (MemoryBlock& result) const;
/** Reads a file into memory as a string.
Attempts to load the entire file as a zero-terminated string.
This makes use of InputStream::readEntireStreamAsString, which can
read either UTF-16 or UTF-8 file formats.
*/
String loadFileAsString() const;
/** Reads the contents of this file as text and splits it into lines, which are
appended to the given StringArray.
*/
void readLines (StringArray& destLines) const;
//==============================================================================
/** Appends a block of binary data to the end of the file.
This will try to write the given buffer to the end of the file.
@returns false if it can't write to the file for some reason
*/
bool appendData (const void* dataToAppend,
size_t numberOfBytes) const;
/** Replaces this file's contents with a given block of data.
This will delete the file and replace it with the given data.
A nice feature of this method is that it's safe - instead of deleting
the file first and then re-writing it, it creates a new temporary file,
writes the data to that, and then moves the new file to replace the existing
file. This means that if the power gets pulled out or something crashes,
you're a lot less likely to end up with a corrupted or unfinished file..
Returns true if the operation succeeds, or false if it fails.
@see appendText
*/
bool replaceWithData (const void* dataToWrite,
size_t numberOfBytes) const;
/** Appends a string to the end of the file.
This will try to append a text string to the file, as either 16-bit unicode
or 8-bit characters in the default system encoding.
It can also write the 'ff fe' unicode header bytes before the text to indicate
the endianness of the file.
Any single \\n characters in the string are replaced with \\r\\n before it is written.
@see replaceWithText
*/
bool appendText (const String& textToAppend,
bool asUnicode = false,
bool writeUnicodeHeaderBytes = false) const;
/** Replaces this file's contents with a given text string.
This will delete the file and replace it with the given text.
A nice feature of this method is that it's safe - instead of deleting
the file first and then re-writing it, it creates a new temporary file,
writes the text to that, and then moves the new file to replace the existing
file. This means that if the power gets pulled out or something crashes,
you're a lot less likely to end up with an empty file..
For an explanation of the parameters here, see the appendText() method.
Returns true if the operation succeeds, or false if it fails.
@see appendText
*/
bool replaceWithText (const String& textToWrite,
bool asUnicode = false,
bool writeUnicodeHeaderBytes = false) const;
/** Attempts to scan the contents of this file and compare it to another file, returning
true if this is possible and they match byte-for-byte.
*/
bool hasIdenticalContentTo (const File& other) const;
//==============================================================================
/** Creates a set of files to represent each file root.
e.g. on Windows this will create files for "c:\", "d:\" etc according
to which ones are available. On the Mac/Linux, this will probably
just add a single entry for "/".
*/
static void findFileSystemRoots (Array<File>& results);
/** Finds the name of the drive on which this file lives.
@returns the volume label of the drive, or an empty string if this isn't possible
*/
String getVolumeLabel() const;
/** Returns the serial number of the volume on which this file lives.
@returns the serial number, or zero if there's a problem doing this
*/
int getVolumeSerialNumber() const;
/** Returns the number of bytes free on the drive that this file lives on.
@returns the number of bytes free, or 0 if there's a problem finding this out
@see getVolumeTotalSize
*/
int64 getBytesFreeOnVolume() const;
/** Returns the total size of the drive that contains this file.
@returns the total number of bytes that the volume can hold
@see getBytesFreeOnVolume
*/
int64 getVolumeTotalSize() const;
/** Returns true if this file is on a CD or DVD drive. */
bool isOnCDRomDrive() const;
/** Returns true if this file is on a hard disk.
This will fail if it's a network drive, but will still be true for
removable hard-disks.
*/
bool isOnHardDisk() const;
/** Returns true if this file is on a removable disk drive.
This might be a usb-drive, a CD-rom, or maybe a network drive.
*/
bool isOnRemovableDrive() const;
//==============================================================================
/** Launches the file as a process.
- if the file is executable, this will run it.
- if it's a document of some kind, it will launch the document with its
default viewer application.
- if it's a folder, it will be opened in Explorer, Finder, or equivalent.
@see revealToUser
*/
bool startAsProcess (const String& parameters = String()) const;
/** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location.
@see startAsProcess
*/
void revealToUser() const;
//==============================================================================
/** A set of types of location that can be passed to the getSpecialLocation() method.
*/
enum SpecialLocationType
{
/** The user's home folder. This is the same as using File ("~"). */
userHomeDirectory,
/** The user's default documents folder. On Windows, this might be the user's
"My Documents" folder. On the Mac it'll be their "Documents" folder. Linux
doesn't tend to have one of these, so it might just return their home folder.
*/
userDocumentsDirectory,
/** The folder that contains the user's desktop objects. */
userDesktopDirectory,
/** The most likely place where a user might store their music files. */
userMusicDirectory,
/** The most likely place where a user might store their movie files. */
userMoviesDirectory,
/** The most likely place where a user might store their picture files. */
userPicturesDirectory,
/** The folder in which applications store their persistent user-specific settings.
On Windows, this might be "\Documents and Settings\username\Application Data".
On the Mac, it might be "~/Library". If you're going to store your settings in here,
always create your own sub-folder to put them in, to avoid making a mess.
*/
userApplicationDataDirectory,
/** An equivalent of the userApplicationDataDirectory folder that is shared by all users
of the computer, rather than just the current user.
On the Mac it'll be "/Library", on Windows, it could be something like
"\Documents and Settings\All Users\Application Data".
Depending on the setup, this folder may be read-only.
*/
commonApplicationDataDirectory,
/** A place to put documents which are shared by all users of the machine.
On Windows this may be somewhere like "C:\Users\Public\Documents", on OSX it
will be something like "/Users/Shared". Other OSes may have no such concept
though, so be careful.
*/
commonDocumentsDirectory,
/** The folder that should be used for temporary files.
Always delete them when you're finished, to keep the user's computer tidy!
*/
tempDirectory,
/** Returns this application's executable file.
If running as a plug-in or DLL, this will (where possible) be the DLL rather than the
host app.
On the mac this will return the unix binary, not the package folder - see
currentApplicationFile for that.
See also invokedExecutableFile, which is similar, but if the exe was launched from a
file link, invokedExecutableFile will return the name of the link.
*/
currentExecutableFile,
/** Returns this application's location.
If running as a plug-in or DLL, this will (where possible) be the DLL rather than the
host app.
On the mac this will return the package folder (if it's in one), not the unix binary
that's inside it - compare with currentExecutableFile.
*/
currentApplicationFile,
/** Returns the file that was invoked to launch this executable.
This may differ from currentExecutableFile if the app was started from e.g. a link - this
will return the name of the link that was used, whereas currentExecutableFile will return
the actual location of the target executable.
*/
invokedExecutableFile,
/** In a plugin, this will return the path of the host executable. */
hostApplicationPath,
#if JUCE_WINDOWS
/** On a Windows machine, returns the location of the Windows/System32 folder. */
windowsSystemDirectory,
#endif
/** The directory in which applications normally get installed.
So on windows, this would be something like "c:\program files", on the
Mac "/Applications", or "/usr" on linux.
*/
globalApplicationsDirectory
};
/** Finds the location of a special type of file or directory, such as a home folder or
documents folder.
@see SpecialLocationType
*/
static File JUCE_CALLTYPE getSpecialLocation (const SpecialLocationType type);
//==============================================================================
/** Returns a temporary file in the system's temp directory.
This will try to return the name of a non-existent temp file.
To get the temp folder, you can use getSpecialLocation (File::tempDirectory).
*/
static File createTempFile (StringRef fileNameEnding);
//==============================================================================
/** Returns the current working directory.
@see setAsCurrentWorkingDirectory
*/
static File getCurrentWorkingDirectory();
/** Sets the current working directory to be this file.
For this to work the file must point to a valid directory.
@returns true if the current directory has been changed.
@see getCurrentWorkingDirectory
*/
bool setAsCurrentWorkingDirectory() const;
//==============================================================================
/** The system-specific file separator character.
On Windows, this will be '\', on Mac/Linux, it'll be '/'
*/
static const juce_wchar separator;
/** The system-specific file separator character, as a string.
On Windows, this will be '\', on Mac/Linux, it'll be '/'
*/
static const String separatorString;
//==============================================================================
/** Returns a version of a filename with any illegal characters removed.
This will return a copy of the given string after removing characters
that are not allowed in a legal filename, and possibly shortening the
string if it's too long.
Because this will remove slashes, don't use it on an absolute pathname - use
createLegalPathName() for that.
@see createLegalPathName
*/
static String createLegalFileName (const String& fileNameToFix);
/** Returns a version of a path with any illegal characters removed.
Similar to createLegalFileName(), but this won't remove slashes, so can
be used on a complete pathname.
@see createLegalFileName
*/
static String createLegalPathName (const String& pathNameToFix);
/** Indicates whether filenames are case-sensitive on the current operating system. */
static bool areFileNamesCaseSensitive();
/** Returns true if the string seems to be a fully-specified absolute path. */
static bool isAbsolutePath (StringRef path);
/** Creates a file that simply contains this string, without doing the sanity-checking
that the normal constructors do.
Best to avoid this unless you really know what you're doing.
*/
static File createFileWithoutCheckingPath (const String& absolutePath) noexcept;
/** Adds a separator character to the end of a path if it doesn't already have one. */
static String addTrailingSeparator (const String& path);
//==============================================================================
/** Tries to create a symbolic link and returns a boolean to indicate success */
bool createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const;
/** Returns true if this file is a link or alias that can be followed using getLinkedTarget(). */
bool isSymbolicLink() const;
/** If this file is a link or alias, this returns the file that it points to.
If the file isn't actually link, it'll just return itself.
*/
File getLinkedTarget() const;
#if JUCE_WINDOWS
/** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */
bool createShortcut (const String& description, const File& linkFileToCreate) const;
/** Windows ONLY - Returns true if this is a win32 .LNK file. */
bool isShortcut() const;
#endif
//==============================================================================
#if JUCE_MAC || JUCE_IOS || DOXYGEN
/** OSX ONLY - Finds the OSType of a file from the its resources. */
OSType getMacOSType() const;
/** OSX ONLY - Returns true if this file is actually a bundle. */
bool isBundle() const;
#endif
#if JUCE_MAC || DOXYGEN
/** OSX ONLY - Adds this file to the OSX dock */
void addToDock() const;
#endif
//==============================================================================
struct NaturalFileComparator
{
NaturalFileComparator (bool shouldPutFoldersFirst) noexcept : foldersFirst (shouldPutFoldersFirst) {}
int compareElements (const File& firstFile, const File& secondFile) const
{
if (foldersFirst && (firstFile.isDirectory() != secondFile.isDirectory()))
return firstFile.isDirectory() ? -1 : 1;
#if NAMES_ARE_CASE_SENSITIVE
return firstFile.getFullPathName().compareNatural (secondFile.getFullPathName(), true);
#else
return firstFile.getFullPathName().compareNatural (secondFile.getFullPathName(), false);
#endif
}
bool foldersFirst;
};
private:
//==============================================================================
String fullPath;
static String parseAbsolutePath (const String&);
String getPathUpToLastSlash() const;
Result createDirectoryInternal (const String&) const;
bool copyInternal (const File&) const;
bool moveInternal (const File&) const;
bool replaceInternal (const File&) const;
bool setFileTimesInternal (int64 m, int64 a, int64 c) const;
void getFileTimesInternal (int64& m, int64& a, int64& c) const;
bool setFileReadOnlyInternal (bool) const;
bool setFileExecutableInternal (bool) const;
};
#endif // JUCE_FILE_H_INCLUDED

+ 119
- 0
source/modules/juce_audio_graph/files/juce_TemporaryFile.cpp View File

@@ -0,0 +1,119 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
static File createTempFile (const File& parentDirectory, String name,
const String& suffix, const int optionFlags)
{
if ((optionFlags & TemporaryFile::useHiddenFile) != 0)
name = "." + name;
return parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & TemporaryFile::putNumbersInBrackets) != 0);
}
TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags)
: temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory),
"temp_" + String::toHexString (Random::getSystemRandom().nextInt()),
suffix, optionFlags))
{
}
TemporaryFile::TemporaryFile (const File& target, const int optionFlags)
: temporaryFile (createTempFile (target.getParentDirectory(),
target.getFileNameWithoutExtension()
+ "_temp" + String::toHexString (Random::getSystemRandom().nextInt()),
target.getFileExtension(), optionFlags)),
targetFile (target)
{
// If you use this constructor, you need to give it a valid target file!
jassert (targetFile != File());
}
TemporaryFile::TemporaryFile (const File& target, const File& temporary)
: temporaryFile (temporary), targetFile (target)
{
}
TemporaryFile::~TemporaryFile()
{
if (! deleteTemporaryFile())
{
/* Failed to delete our temporary file! The most likely reason for this would be
that you've not closed an output stream that was being used to write to file.
If you find that something beyond your control is changing permissions on
your temporary files and preventing them from being deleted, you may want to
call TemporaryFile::deleteTemporaryFile() to detect those error cases and
handle them appropriately.
*/
jassertfalse;
}
}
//==============================================================================
bool TemporaryFile::overwriteTargetFileWithTemporary() const
{
// This method only works if you created this object with the constructor
// that takes a target file!
jassert (targetFile != File());
if (temporaryFile.exists())
{
// Have a few attempts at overwriting the file before giving up..
for (int i = 5; --i >= 0;)
{
if (temporaryFile.replaceFileIn (targetFile))
return true;
Thread::sleep (100);
}
}
else
{
// There's no temporary file to use. If your write failed, you should
// probably check, and not bother calling this method.
jassertfalse;
}
return false;
}
bool TemporaryFile::deleteTemporaryFile() const
{
// Have a few attempts at deleting the file before giving up..
for (int i = 5; --i >= 0;)
{
if (temporaryFile.deleteFile())
return true;
Thread::sleep (50);
}
return false;
}

+ 171
- 0
source/modules/juce_audio_graph/files/juce_TemporaryFile.h View File

@@ -0,0 +1,171 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_TEMPORARYFILE_H_INCLUDED
#define JUCE_TEMPORARYFILE_H_INCLUDED
//==============================================================================
/**
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)
};
#endif // JUCE_TEMPORARYFILE_H_INCLUDED

+ 3
- 30
source/modules/juce_audio_graph/juce_audio_graph.cpp View File

@@ -18,13 +18,6 @@
#include "juce_audio_graph.h" #include "juce_audio_graph.h"
// #include "AppConfig.h"
// #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
// #include "juce_audio_processors.h"
// #include <juce_gui_extra/juce_gui_extra.h>
#include <locale> #include <locale>
#include <cctype> #include <cctype>
#include <cstdarg> #include <cstdarg>
@@ -43,33 +36,13 @@ namespace juce2
// #include "streams/juce_InputStream.cpp" // #include "streams/juce_InputStream.cpp"
// #include "streams/juce_OutputStream.cpp" // #include "streams/juce_OutputStream.cpp"
// static inline bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list,
// const PluginDescription& desc)
// {
// for (int i = list.size(); --i >= 0;)
// if (list.getUnchecked(i)->isDuplicateOf (desc))
// return true;
//
// return false;
// }
#include "midi/juce_MidiBuffer.cpp" #include "midi/juce_MidiBuffer.cpp"
#include "midi/juce_MidiMessage.cpp" #include "midi/juce_MidiMessage.cpp"
// #include "format/juce_AudioPluginFormat.cpp"
// #include "format/juce_AudioPluginFormatManager.cpp"
#include "processors/juce_AudioProcessor.cpp" #include "processors/juce_AudioProcessor.cpp"
#include "processors/juce_AudioProcessorGraph.cpp" #include "processors/juce_AudioProcessorGraph.cpp"
// #include "processors/juce_GenericAudioProcessorEditor.cpp"
// #include "processors/juce_PluginDescription.cpp"
// #include "format_types/juce_LADSPAPluginFormat.cpp"
// #include "format_types/juce_VSTPluginFormat.cpp"
// #include "format_types/juce_VST3PluginFormat.cpp"
// #include "format_types/juce_AudioUnitPluginFormat.mm"
// #include "scanning/juce_KnownPluginList.cpp"
// #include "scanning/juce_PluginDirectoryScanner.cpp"
// #include "scanning/juce_PluginListComponent.cpp"
// #include "utilities/juce_AudioProcessorValueTreeState.cpp"
// #include "utilities/juce_AudioProcessorParameters.cpp"
#include "xml/juce_XmlElement.cpp"
#include "xml/juce_XmlDocument.cpp"
} }

+ 30
- 19
source/modules/juce_audio_graph/juce_audio_graph.h View File

@@ -109,11 +109,18 @@
namespace juce2 namespace juce2
{ {
class DynamicObject;
class File; class File;
class FileInputStream;
class FileOutputStream;
class InputSource;
class InputStream;
class MidiMessage; class MidiMessage;
class MemoryBlock; class MemoryBlock;
class OutputStream; class OutputStream;
class Result;
class StringRef; class StringRef;
class XmlElement;
#include "memory/juce_Memory.h" #include "memory/juce_Memory.h"
#include "maths/juce_MathsFunctions.h" #include "maths/juce_MathsFunctions.h"
@@ -123,47 +130,51 @@ class StringRef;
#include "text/juce_CharPointer_UTF8.h" #include "text/juce_CharPointer_UTF8.h"
#include "text/juce_String.h" #include "text/juce_String.h"
#include "text/juce_StringRef.h"
#include "memory/juce_HeapBlock.h" #include "memory/juce_HeapBlock.h"
#include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_Array.h"
#include "text/juce_StringArray.h"
#include "text/juce_StringRef.h"
#include "memory/juce_MemoryBlock.h" #include "memory/juce_MemoryBlock.h"
#include "memory/juce_ReferenceCountedObject.h" #include "memory/juce_ReferenceCountedObject.h"
#include "text/juce_Identifier.h"
#include "text/juce_NewLine.h" #include "text/juce_NewLine.h"
#include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_Array.h"
#include "containers/juce_LinkedListPointer.h"
#include "containers/juce_OwnedArray.h" #include "containers/juce_OwnedArray.h"
#include "containers/juce_ReferenceCountedArray.h" #include "containers/juce_ReferenceCountedArray.h"
#include "containers/juce_SortedSet.h" #include "containers/juce_SortedSet.h"
// #include "containers/juce_Variant.h"
// #include "containers/juce_NamedValueSet.h"
#include "containers/juce_Variant.h"
#include "containers/juce_NamedValueSet.h"
#include "streams/juce_InputSource.h"
#include "streams/juce_InputStream.h" #include "streams/juce_InputStream.h"
#include "streams/juce_OutputStream.h" #include "streams/juce_OutputStream.h"
#include "streams/juce_MemoryOutputStream.h" #include "streams/juce_MemoryOutputStream.h"
#include "misc/juce_Result.h"
#include "text/juce_StringPool.h"
#include "files/juce_File.h"
#include "streams/juce_FileInputStream.h"
#include "streams/juce_FileInputSource.h"
#include "buffers/juce_AudioSampleBuffer.h" #include "buffers/juce_AudioSampleBuffer.h"
#include "midi/juce_MidiBuffer.h" #include "midi/juce_MidiBuffer.h"
#include "midi/juce_MidiMessage.h" #include "midi/juce_MidiMessage.h"
class AudioProcessor; class AudioProcessor;
#include "processors/juce_AudioPlayHead.h" #include "processors/juce_AudioPlayHead.h"
// #include "processors/juce_AudioProcessorListener.h"
// #include "processors/juce_AudioProcessorParameter.h"
#include "processors/juce_AudioProcessor.h" #include "processors/juce_AudioProcessor.h"
// #include "processors/juce_PluginDescription.h"
// #include "processors/juce_AudioPluginInstance.h"
#include "processors/juce_AudioProcessorGraph.h" #include "processors/juce_AudioProcessorGraph.h"
// #include "format/juce_AudioPluginFormat.h"
// #include "format/juce_AudioPluginFormatManager.h"
// #include "scanning/juce_KnownPluginList.h"
// #include "utilities/juce_AudioProcessorValueTreeState.h"
// #include "utilities/juce_AudioProcessorParameterWithID.h"
// #include "utilities/juce_AudioParameterFloat.h"
// #include "utilities/juce_AudioParameterInt.h"
// #include "utilities/juce_AudioParameterBool.h"
// #include "utilities/juce_AudioParameterChoice.h"
#include "xml/juce_XmlElement.h"
#include "xml/juce_XmlDocument.h"
} }


+ 24
- 0
source/modules/juce_audio_graph/main.cpp View File

@@ -0,0 +1,24 @@

#include "juce_audio_graph.h"
#include <iostream>

using namespace juce2;

int main()
{
String x = "haha";
std::cout << x << std::endl;

Atomic<float> a;
CharPointer_UTF8 c("c");
HeapBlock<String> hs;
MemoryBlock m;
Array<CharPointer_UTF8> ar;
OwnedArray<String> ows;
AudioSampleBuffer as;
MidiBuffer mb;
MidiMessage ms;
AudioProcessorGraph g;

return 0;
}

+ 423
- 0
source/modules/juce_audio_graph/memory/juce_ReferenceCountedObject.h View File

@@ -0,0 +1,423 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED
#define JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED
//==============================================================================
/**
A base class which provides methods for reference-counting.
To add reference-counting to a class, derive it from this class, and
use the ReferenceCountedObjectPtr class to point to it.
e.g. @code
class MyClass : public ReferenceCountedObject
{
void foo();
// This is a neat way of declaring a typedef for a pointer class,
// rather than typing out the full templated name each time..
typedef ReferenceCountedObjectPtr<MyClass> Ptr;
};
MyClass::Ptr p = new MyClass();
MyClass::Ptr p2 = p;
p = nullptr;
p2->foo();
@endcode
Once a new ReferenceCountedObject has been assigned to a pointer, be
careful not to delete the object manually.
This class uses an Atomic<int> value to hold the reference count, so that it
the pointers can be passed between threads safely. For a faster but non-thread-safe
version, use SingleThreadedReferenceCountedObject instead.
@see ReferenceCountedObjectPtr, ReferenceCountedArray, SingleThreadedReferenceCountedObject
*/
class JUCE_API ReferenceCountedObject
{
public:
//==============================================================================
/** Increments the object's reference count.
This is done automatically by the smart pointer, but is public just
in case it's needed for nefarious purposes.
*/
void incReferenceCount() noexcept
{
++refCount;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will be deleted.
*/
void decReferenceCount() noexcept
{
jassert (getReferenceCount() > 0);
if (--refCount == 0)
delete this;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will not be deleted, but this method
will return true, allowing the caller to take care of deletion.
*/
bool decReferenceCountWithoutDeleting() noexcept
{
jassert (getReferenceCount() > 0);
return --refCount == 0;
}
/** Returns the object's current reference count. */
int getReferenceCount() const noexcept { return refCount.get(); }
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
ReferenceCountedObject() {}
/** Destructor. */
virtual ~ReferenceCountedObject()
{
// it's dangerous to delete an object that's still referenced by something else!
jassert (getReferenceCount() == 0);
}
/** Resets the reference count to zero without deleting the object.
You should probably never need to use this!
*/
void resetReferenceCount() noexcept
{
refCount = 0;
}
private:
//==============================================================================
Atomic <int> refCount;
JUCE_DECLARE_NON_COPYABLE (ReferenceCountedObject)
};
//==============================================================================
/**
Adds reference-counting to an object.
This is effectively a version of the ReferenceCountedObject class, but which
uses a non-atomic counter, and so is not thread-safe (but which will be more
efficient).
For more details on how to use it, see the ReferenceCountedObject class notes.
@see ReferenceCountedObject, ReferenceCountedObjectPtr, ReferenceCountedArray
*/
class JUCE_API SingleThreadedReferenceCountedObject
{
public:
//==============================================================================
/** Increments the object's reference count.
This is done automatically by the smart pointer, but is public just
in case it's needed for nefarious purposes.
*/
void incReferenceCount() noexcept
{
++refCount;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will be deleted.
*/
void decReferenceCount() noexcept
{
jassert (getReferenceCount() > 0);
if (--refCount == 0)
delete this;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will not be deleted, but this method
will return true, allowing the caller to take care of deletion.
*/
bool decReferenceCountWithoutDeleting() noexcept
{
jassert (getReferenceCount() > 0);
return --refCount == 0;
}
/** Returns the object's current reference count. */
int getReferenceCount() const noexcept { return refCount; }
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
SingleThreadedReferenceCountedObject() : refCount (0) {}
/** Destructor. */
virtual ~SingleThreadedReferenceCountedObject()
{
// it's dangerous to delete an object that's still referenced by something else!
jassert (getReferenceCount() == 0);
}
private:
//==============================================================================
int refCount;
JUCE_DECLARE_NON_COPYABLE (SingleThreadedReferenceCountedObject)
};
//==============================================================================
/**
A smart-pointer class which points to a reference-counted object.
The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave.
When using this class, you'll probably want to create a typedef to abbreviate the full
templated name - e.g.
@code
struct MyClass : public ReferenceCountedObject
{
typedef ReferenceCountedObjectPtr<MyClass> Ptr;
...
@endcode
@see ReferenceCountedObject, ReferenceCountedObjectArray
*/
template <class ReferenceCountedObjectClass>
class ReferenceCountedObjectPtr
{
public:
/** The class being referenced by this pointer. */
typedef ReferenceCountedObjectClass ReferencedType;
//==============================================================================
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr() noexcept
: referencedObject (nullptr)
{
}
/** Creates a pointer to an object.
This will increment the object's reference-count.
*/
ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept
: referencedObject (refCountedObject)
{
incIfNotNull (refCountedObject);
}
#if JUCE_COMPILER_SUPPORTS_NULLPTR
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr (decltype (nullptr)) noexcept
: referencedObject (nullptr)
{
}
#endif
/** Copies another pointer.
This will increment the object's reference-count.
*/
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept
: referencedObject (other.referencedObject)
{
incIfNotNull (referencedObject);
}
/** Copies another pointer.
This will increment the object's reference-count (if it is non-null).
*/
template <typename Convertible>
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<Convertible>& other) noexcept
: referencedObject (static_cast<ReferencedType*> (other.get()))
{
incIfNotNull (referencedObject);
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other)
{
return operator= (other.referencedObject);
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
template <typename Convertible>
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr<Convertible>& other)
{
return operator= (static_cast<ReferencedType*> (other.get()));
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
ReferenceCountedObjectPtr& operator= (ReferencedType* const newObject)
{
if (referencedObject != newObject)
{
incIfNotNull (newObject);
ReferencedType* const oldObject = referencedObject;
referencedObject = newObject;
decIfNotNull (oldObject);
}
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept
: referencedObject (other.referencedObject)
{
other.referencedObject = nullptr;
}
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other)
{
std::swap (referencedObject, other.referencedObject);
return *this;
}
#endif
/** Destructor.
This will decrement the object's reference-count, which will cause the
object to be deleted when the ref-count hits zero.
*/
~ReferenceCountedObjectPtr()
{
decIfNotNull (referencedObject);
}
//==============================================================================
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
operator ReferencedType*() const noexcept { return referencedObject; }
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* get() const noexcept { return referencedObject; }
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* getObject() const noexcept { return referencedObject; }
// the -> operator is called on the referenced object
ReferencedType* operator->() const noexcept
{
jassert (referencedObject != nullptr); // null pointer method call!
return referencedObject;
}
private:
//==============================================================================
ReferencedType* referencedObject;
static void incIfNotNull (ReferencedType* o) noexcept
{
if (o != nullptr)
o->incReferenceCount();
}
static void decIfNotNull (ReferencedType* o) noexcept
{
if (o != nullptr && o->decReferenceCountWithoutDeleting())
delete o;
}
};
//==============================================================================
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ReferenceCountedObjectClass>
bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, ReferenceCountedObjectClass* const object2) noexcept
{
return object1.get() == object2;
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ReferenceCountedObjectClass>
bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
{
return object1.get() == object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ReferenceCountedObjectClass>
bool operator== (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
{
return object1 == object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ReferenceCountedObjectClass>
bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectClass* object2) noexcept
{
return object1.get() != object2;
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ReferenceCountedObjectClass>
bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
{
return object1.get() != object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ReferenceCountedObjectClass>
bool operator!= (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
{
return object1 != object2.get();
}
#endif // JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED

+ 85
- 0
source/modules/juce_audio_graph/misc/juce_Result.cpp View File

@@ -0,0 +1,85 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
Result::Result() noexcept {}
Result::Result (const String& message) noexcept
: errorMessage (message)
{
}
Result::Result (const Result& other)
: errorMessage (other.errorMessage)
{
}
Result& Result::operator= (const Result& other)
{
errorMessage = other.errorMessage;
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
Result::Result (Result&& other) noexcept
: errorMessage (static_cast<String&&> (other.errorMessage))
{
}
Result& Result::operator= (Result&& other) noexcept
{
errorMessage = static_cast<String&&> (other.errorMessage);
return *this;
}
#endif
bool Result::operator== (const Result& other) const noexcept
{
return errorMessage == other.errorMessage;
}
bool Result::operator!= (const Result& other) const noexcept
{
return errorMessage != other.errorMessage;
}
Result Result::fail (const String& errorMessage) noexcept
{
return Result (errorMessage.isEmpty() ? "Unknown Error" : errorMessage);
}
const String& Result::getErrorMessage() const noexcept
{
return errorMessage;
}
bool Result::wasOk() const noexcept { return errorMessage.isEmpty(); }
Result::operator bool() const noexcept { return errorMessage.isEmpty(); }
bool Result::failed() const noexcept { return errorMessage.isNotEmpty(); }
bool Result::operator!() const noexcept { return errorMessage.isNotEmpty(); }

+ 127
- 0
source/modules/juce_audio_graph/misc/juce_Result.h View File

@@ -0,0 +1,127 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_RESULT_H_INCLUDED
#define JUCE_RESULT_H_INCLUDED
//==============================================================================
/**
Represents the 'success' or 'failure' of an operation, and holds an associated
error message to describe the error when there's a failure.
E.g.
@code
Result myOperation()
{
if (doSomeKindOfFoobar())
return Result::ok();
else
return Result::fail ("foobar didn't work!");
}
const Result result (myOperation());
if (result.wasOk())
{
...it's all good...
}
else
{
warnUserAboutFailure ("The foobar operation failed! Error message was: "
+ result.getErrorMessage());
}
@endcode
*/
class JUCE_API Result
{
public:
//==============================================================================
/** Creates and returns a 'successful' result. */
static Result ok() noexcept { return Result(); }
/** Creates a 'failure' result.
If you pass a blank error message in here, a default "Unknown Error" message
will be used instead.
*/
static Result fail (const String& errorMessage) noexcept;
//==============================================================================
/** Returns true if this result indicates a success. */
bool wasOk() const noexcept;
/** Returns true if this result indicates a failure.
You can use getErrorMessage() to retrieve the error message associated
with the failure.
*/
bool failed() const noexcept;
/** Returns true if this result indicates a success.
This is equivalent to calling wasOk().
*/
operator bool() const noexcept;
/** Returns true if this result indicates a failure.
This is equivalent to calling failed().
*/
bool operator!() const noexcept;
/** Returns the error message that was set when this result was created.
For a successful result, this will be an empty string;
*/
const String& getErrorMessage() const noexcept;
//==============================================================================
Result (const Result&);
Result& operator= (const Result&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
Result (Result&&) noexcept;
Result& operator= (Result&&) noexcept;
#endif
bool operator== (const Result& other) const noexcept;
bool operator!= (const Result& other) const noexcept;
private:
String errorMessage;
// The default constructor is not for public use!
// Instead, use Result::ok() or Result::fail()
Result() noexcept;
explicit Result (const String&) noexcept;
// These casts are private to prevent people trying to use the Result object in numeric contexts
operator int() const;
operator void*() const;
};
#endif // JUCE_RESULT_H_INCLUDED

+ 0
- 2
source/modules/juce_audio_graph/processors/juce_AudioProcessorGraph.h View File

@@ -71,7 +71,6 @@ public:
/** The actual processor object that this node represents. */ /** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor; } AudioProcessor* getProcessor() const noexcept { return processor; }
#if 0
/** A set of user-definable properties that are associated with this node. /** A set of user-definable properties that are associated with this node.
This can be used to attach values to the node for whatever purpose seems This can be used to attach values to the node for whatever purpose seems
@@ -79,7 +78,6 @@ public:
is displaying the nodes on-screen. is displaying the nodes on-screen.
*/ */
NamedValueSet properties; NamedValueSet properties;
#endif
//============================================================================== //==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */ /** A convenient typedef for referring to a pointer to a node object. */


+ 58
- 0
source/modules/juce_audio_graph/streams/juce_FileInputSource.cpp View File

@@ -0,0 +1,58 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
FileInputSource::FileInputSource (const File& f, bool useFileTimeInHash)
: file (f), useFileTimeInHashGeneration (useFileTimeInHash)
{
}
FileInputSource::~FileInputSource()
{
}
InputStream* FileInputSource::createInputStream()
{
return file.createInputStream();
}
InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath)
{
return file.getSiblingFile (relatedItemPath).createInputStream();
}
int64 FileInputSource::hashCode() const
{
int64 h = file.hashCode();
if (useFileTimeInHashGeneration)
h ^= file.getLastModificationTime().toMilliseconds();
return h;
}

+ 68
- 0
source/modules/juce_audio_graph/streams/juce_FileInputSource.h View File

@@ -0,0 +1,68 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILEINPUTSOURCE_H_INCLUDED
#define JUCE_FILEINPUTSOURCE_H_INCLUDED
//==============================================================================
/**
A type of InputSource that represents a normal file.
@see InputSource
*/
class JUCE_API FileInputSource : public InputSource
{
public:
//==============================================================================
/** Creates a FileInputSource for a file.
If the useFileTimeInHashGeneration parameter is true, then this object's
hashCode() method will incorporate the file time into its hash code; if
false, only the file name will be used for the hash.
*/
FileInputSource (const File& file, bool useFileTimeInHashGeneration = false);
/** Destructor. */
~FileInputSource();
InputStream* createInputStream();
InputStream* createInputStreamFor (const String& relatedItemPath);
int64 hashCode() const;
private:
//==============================================================================
const File file;
bool useFileTimeInHashGeneration;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputSource)
};
#endif // JUCE_FILEINPUTSOURCE_H_INCLUDED

+ 86
- 0
source/modules/juce_audio_graph/streams/juce_FileInputStream.cpp View File

@@ -0,0 +1,86 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
int64 juce_fileSetPosition (void* handle, int64 pos);
//==============================================================================
FileInputStream::FileInputStream (const File& f)
: file (f),
fileHandle (nullptr),
currentPosition (0),
status (Result::ok())
{
openHandle();
}
int64 FileInputStream::getTotalLength()
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
return file.getSize();
}
int FileInputStream::read (void* buffer, int bytesToRead)
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
// The buffer should never be null, and a negative size is probably a
// sign that something is broken!
jassert (buffer != nullptr && bytesToRead >= 0);
const size_t num = readInternal (buffer, (size_t) bytesToRead);
currentPosition += (int64) num;
return (int) num;
}
bool FileInputStream::isExhausted()
{
return currentPosition >= getTotalLength();
}
int64 FileInputStream::getPosition()
{
return currentPosition;
}
bool FileInputStream::setPosition (int64 pos)
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
if (pos != currentPosition)
currentPosition = juce_fileSetPosition (fileHandle, pos);
return currentPosition == pos;
}

+ 98
- 0
source/modules/juce_audio_graph/streams/juce_FileInputStream.h View File

@@ -0,0 +1,98 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILEINPUTSTREAM_H_INCLUDED
#define JUCE_FILEINPUTSTREAM_H_INCLUDED
//==============================================================================
/**
An input stream that reads from a local file.
@see InputStream, FileOutputStream, File::createInputStream
*/
class JUCE_API FileInputStream : public InputStream
{
public:
//==============================================================================
/** Creates a FileInputStream to read from the given file.
After creating a FileInputStream, you should use openedOk() or failedToOpen()
to make sure that it's OK before trying to read from it! If it failed, you
can call getStatus() to get more error information.
*/
explicit FileInputStream (const File& fileToRead);
/** Destructor. */
~FileInputStream();
//==============================================================================
/** Returns the file that this stream is reading from. */
const File& getFile() const noexcept { 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 reading from 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(); }
//==============================================================================
int64 getTotalLength() override;
int read (void*, int) override;
bool isExhausted() override;
int64 getPosition() override;
bool setPosition (int64) override;
private:
//==============================================================================
const File file;
void* fileHandle;
int64 currentPosition;
Result status;
void openHandle();
size_t readInternal (void*, size_t);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream)
};
#endif // JUCE_FILEINPUTSTREAM_H_INCLUDED

+ 80
- 0
source/modules/juce_audio_graph/streams/juce_InputSource.h View File

@@ -0,0 +1,80 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_INPUTSOURCE_H_INCLUDED
#define JUCE_INPUTSOURCE_H_INCLUDED
//==============================================================================
/**
A lightweight object that can create a stream to read some kind of resource.
This may be used to refer to a file, or some other kind of source, allowing a
caller to create an input stream that can read from it when required.
@see FileInputSource
*/
class JUCE_API InputSource
{
public:
//==============================================================================
InputSource() noexcept {}
/** Destructor. */
virtual ~InputSource() {}
//==============================================================================
/** Returns a new InputStream to read this item.
@returns an inputstream that the caller will delete, or nullptr if
the filename isn't found.
*/
virtual InputStream* createInputStream() = 0;
/** Returns a new InputStream to read an item, relative.
@param relatedItemPath the relative pathname of the resource that is required
@returns an inputstream that the caller will delete, or nullptr if
the item isn't found.
*/
virtual InputStream* createInputStreamFor (const String& relatedItemPath) = 0;
/** Returns a hash code that uniquely represents this item.
*/
virtual int64 hashCode() const = 0;
private:
//==============================================================================
JUCE_LEAK_DETECTOR (InputSource)
};
#endif // JUCE_INPUTSOURCE_H_INCLUDED

+ 79
- 0
source/modules/juce_audio_graph/text/juce_Identifier.cpp View File

@@ -0,0 +1,79 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
Identifier::Identifier() noexcept {}
Identifier::~Identifier() noexcept {}
Identifier::Identifier (const Identifier& other) noexcept : name (other.name) {}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
Identifier::Identifier (Identifier&& other) noexcept : name (static_cast<String&&> (other.name)) {}
Identifier& Identifier::operator= (Identifier&& other) noexcept
{
name = static_cast<String&&> (other.name);
return *this;
}
#endif
Identifier& Identifier::operator= (const Identifier& other) noexcept
{
name = other.name;
return *this;
}
Identifier::Identifier (const String& nm)
: name (StringPool::getGlobalPool().getPooledString (nm))
{
// An Identifier cannot be created from an empty string!
jassert (nm.isNotEmpty());
}
Identifier::Identifier (const char* nm)
: name (StringPool::getGlobalPool().getPooledString (nm))
{
// An Identifier cannot be created from an empty string!
jassert (nm != nullptr && nm[0] != 0);
}
Identifier::Identifier (String::CharPointerType start, String::CharPointerType end)
: name (StringPool::getGlobalPool().getPooledString (start, end))
{
// An Identifier cannot be created from an empty string!
jassert (start < end);
}
Identifier Identifier::null;
bool Identifier::isValidIdentifier (const String& possibleIdentifier) noexcept
{
return possibleIdentifier.isNotEmpty()
&& possibleIdentifier.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:#@$%");
}

+ 142
- 0
source/modules/juce_audio_graph/text/juce_Identifier.h View File

@@ -0,0 +1,142 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_IDENTIFIER_H_INCLUDED
#define JUCE_IDENTIFIER_H_INCLUDED
//==============================================================================
/**
Represents a string identifier, designed for accessing properties by name.
Comparing two Identifier objects is very fast (an O(1) operation), but creating
them can be slower than just using a String directly, so the optimal way to use them
is to keep some static Identifier objects for the things you use often.
@see NamedValueSet, ValueTree
*/
class JUCE_API Identifier
{
public:
/** Creates a null identifier. */
Identifier() noexcept;
/** Creates an identifier with a specified name.
Because this name may need to be used in contexts such as script variables or XML
tags, it must only contain ascii letters and digits, or the underscore character.
*/
Identifier (const char* name);
/** Creates an identifier with a specified name.
Because this name may need to be used in contexts such as script variables or XML
tags, it must only contain ascii letters and digits, or the underscore character.
*/
Identifier (const String& name);
/** Creates an identifier with a specified name.
Because this name may need to be used in contexts such as script variables or XML
tags, it must only contain ascii letters and digits, or the underscore character.
*/
Identifier (String::CharPointerType nameStart, String::CharPointerType nameEnd);
/** Creates a copy of another identifier. */
Identifier (const Identifier& other) noexcept;
/** Creates a copy of another identifier. */
Identifier& operator= (const Identifier& other) noexcept;
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
/** Creates a copy of another identifier. */
Identifier (Identifier&& other) noexcept;
/** Creates a copy of another identifier. */
Identifier& operator= (Identifier&& other) noexcept;
#endif
/** Destructor */
~Identifier() noexcept;
/** Compares two identifiers. This is a very fast operation. */
inline bool operator== (const Identifier& other) const noexcept { return name.getCharPointer() == other.name.getCharPointer(); }
/** Compares two identifiers. This is a very fast operation. */
inline bool operator!= (const Identifier& other) const noexcept { return name.getCharPointer() != other.name.getCharPointer(); }
/** Compares the identifier with a string. */
inline bool operator== (StringRef other) const noexcept { return name == other; }
/** Compares the identifier with a string. */
inline bool operator!= (StringRef other) const noexcept { return name != other; }
/** Compares the identifier with a string. */
inline bool operator< (StringRef other) const noexcept { return name < other; }
/** Compares the identifier with a string. */
inline bool operator<= (StringRef other) const noexcept { return name <= other; }
/** Compares the identifier with a string. */
inline bool operator> (StringRef other) const noexcept { return name > other; }
/** Compares the identifier with a string. */
inline bool operator>= (StringRef other) const noexcept { return name >= other; }
/** Returns this identifier as a string. */
const String& toString() const noexcept { return name; }
/** Returns this identifier's raw string pointer. */
operator String::CharPointerType() const noexcept { return name.getCharPointer(); }
/** Returns this identifier's raw string pointer. */
String::CharPointerType getCharPointer() const noexcept { return name.getCharPointer(); }
/** Returns this identifier as a StringRef. */
operator StringRef() const noexcept { return name; }
/** Returns true if this Identifier is not null */
bool isValid() const noexcept { return name.isNotEmpty(); }
/** Returns true if this Identifier is null */
bool isNull() const noexcept { return name.isEmpty(); }
/** A null identifier. */
static Identifier null;
/** Checks a given string for characters that might not be valid in an Identifier.
Since Identifiers are used as a script variables and XML attributes, they should only contain
alphanumeric characters, underscores, or the '-' and ':' characters.
*/
static bool isValidIdentifier (const String& possibleIdentifier) noexcept;
private:
String name;
};
#endif // JUCE_IDENTIFIER_H_INCLUDED

+ 508
- 0
source/modules/juce_audio_graph/text/juce_StringArray.cpp View File

@@ -0,0 +1,508 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
StringArray::StringArray() noexcept
{
}
StringArray::StringArray (const StringArray& other)
: strings (other.strings)
{
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
StringArray::StringArray (StringArray&& other) noexcept
: strings (static_cast<Array <String>&&> (other.strings))
{
}
#endif
StringArray::StringArray (const String& firstValue)
{
strings.add (firstValue);
}
StringArray::StringArray (const String* initialStrings, int numberOfStrings)
{
strings.addArray (initialStrings, numberOfStrings);
}
StringArray::StringArray (const char* const* initialStrings)
{
strings.addNullTerminatedArray (initialStrings);
}
StringArray::StringArray (const char* const* initialStrings, int numberOfStrings)
{
strings.addArray (initialStrings, numberOfStrings);
}
StringArray::StringArray (const wchar_t* const* initialStrings)
{
strings.addNullTerminatedArray (initialStrings);
}
StringArray::StringArray (const wchar_t* const* initialStrings, int numberOfStrings)
{
strings.addArray (initialStrings, numberOfStrings);
}
StringArray& StringArray::operator= (const StringArray& other)
{
strings = other.strings;
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
StringArray& StringArray::operator= (StringArray&& other) noexcept
{
strings = static_cast<Array<String>&&> (other.strings);
return *this;
}
#endif
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
StringArray::StringArray (const std::initializer_list<const char*>& stringList)
{
strings.addArray (stringList);
}
#endif
StringArray::~StringArray()
{
}
bool StringArray::operator== (const StringArray& other) const noexcept
{
return strings == other.strings;
}
bool StringArray::operator!= (const StringArray& other) const noexcept
{
return ! operator== (other);
}
void StringArray::swapWith (StringArray& other) noexcept
{
strings.swapWith (other.strings);
}
void StringArray::clear()
{
strings.clear();
}
void StringArray::clearQuick()
{
strings.clearQuick();
}
const String& StringArray::operator[] (const int index) const noexcept
{
if (isPositiveAndBelow (index, strings.size()))
return strings.getReference (index);
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
return String::empty;
#else
static String empty;
return empty;
#endif
}
String& StringArray::getReference (const int index) noexcept
{
return strings.getReference (index);
}
void StringArray::add (const String& newString)
{
strings.add (newString);
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
void StringArray::add (String&& stringToAdd)
{
strings.add (static_cast<String&&> (stringToAdd));
}
#endif
void StringArray::insert (const int index, const String& newString)
{
strings.insert (index, newString);
}
bool StringArray::addIfNotAlreadyThere (const String& newString, const bool ignoreCase)
{
if (contains (newString, ignoreCase))
return false;
add (newString);
return true;
}
void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd)
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
numElementsToAdd = otherArray.size() - startIndex;
while (--numElementsToAdd >= 0)
strings.add (otherArray.strings.getReference (startIndex++));
}
void StringArray::mergeArray (const StringArray& otherArray, const bool ignoreCase)
{
for (int i = 0; i < otherArray.size(); ++i)
addIfNotAlreadyThere (otherArray[i], ignoreCase);
}
void StringArray::set (const int index, const String& newString)
{
strings.set (index, newString);
}
bool StringArray::contains (StringRef stringToLookFor, const bool ignoreCase) const
{
return indexOf (stringToLookFor, ignoreCase) >= 0;
}
int StringArray::indexOf (StringRef stringToLookFor, const bool ignoreCase, int i) const
{
if (i < 0)
i = 0;
const int numElements = size();
if (ignoreCase)
{
for (; i < numElements; ++i)
if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
return i;
}
else
{
for (; i < numElements; ++i)
if (stringToLookFor == strings.getReference (i))
return i;
}
return -1;
}
void StringArray::move (const int currentIndex, const int newIndex) noexcept
{
strings.move (currentIndex, newIndex);
}
//==============================================================================
void StringArray::remove (const int index)
{
strings.remove (index);
}
void StringArray::removeString (StringRef stringToRemove, const bool ignoreCase)
{
if (ignoreCase)
{
for (int i = size(); --i >= 0;)
if (strings.getReference(i).equalsIgnoreCase (stringToRemove))
strings.remove (i);
}
else
{
for (int i = size(); --i >= 0;)
if (stringToRemove == strings.getReference (i))
strings.remove (i);
}
}
void StringArray::removeRange (int startIndex, int numberToRemove)
{
strings.removeRange (startIndex, numberToRemove);
}
//==============================================================================
void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings)
{
if (removeWhitespaceStrings)
{
for (int i = size(); --i >= 0;)
if (! strings.getReference(i).containsNonWhitespaceChars())
strings.remove (i);
}
else
{
for (int i = size(); --i >= 0;)
if (strings.getReference(i).isEmpty())
strings.remove (i);
}
}
void StringArray::trim()
{
for (int i = size(); --i >= 0;)
{
String& s = strings.getReference(i);
s = s.trim();
}
}
//==============================================================================
struct InternalStringArrayComparator_CaseSensitive
{
static int compareElements (String& s1, String& s2) noexcept { return s1.compare (s2); }
};
struct InternalStringArrayComparator_CaseInsensitive
{
static int compareElements (String& s1, String& s2) noexcept { return s1.compareIgnoreCase (s2); }
};
struct InternalStringArrayComparator_Natural
{
static int compareElements (String& s1, String& s2) noexcept { return s1.compareNatural (s2); }
};
void StringArray::sort (const bool ignoreCase)
{
if (ignoreCase)
{
InternalStringArrayComparator_CaseInsensitive comp;
strings.sort (comp);
}
else
{
InternalStringArrayComparator_CaseSensitive comp;
strings.sort (comp);
}
}
void StringArray::sortNatural()
{
InternalStringArrayComparator_Natural comp;
strings.sort (comp);
}
//==============================================================================
String StringArray::joinIntoString (StringRef separator, int start, int numberToJoin) const
{
const int last = (numberToJoin < 0) ? size()
: jmin (size(), start + numberToJoin);
if (start < 0)
start = 0;
if (start >= last)
return String();
if (start == last - 1)
return strings.getReference (start);
const size_t separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType);
size_t bytesNeeded = separatorBytes * (size_t) (last - start - 1);
for (int i = start; i < last; ++i)
bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
String result;
result.preallocateBytes (bytesNeeded);
String::CharPointerType dest (result.getCharPointer());
while (start < last)
{
const String& s = strings.getReference (start);
if (! s.isEmpty())
dest.writeAll (s.getCharPointer());
if (++start < last && separatorBytes > 0)
dest.writeAll (separator.text);
}
dest.writeNull();
return result;
}
int StringArray::addTokens (StringRef text, const bool preserveQuotedStrings)
{
return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : "");
}
int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters)
{
int num = 0;
if (text.isNotEmpty())
{
for (String::CharPointerType t (text.text);;)
{
String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t,
breakCharacters.text,
quoteCharacters.text));
strings.add (String (t, tokenEnd));
++num;
if (tokenEnd.isEmpty())
break;
t = ++tokenEnd;
}
}
return num;
}
int StringArray::addLines (StringRef sourceText)
{
int numLines = 0;
String::CharPointerType text (sourceText.text);
bool finished = text.isEmpty();
while (! finished)
{
for (String::CharPointerType startOfLine (text);;)
{
const String::CharPointerType endOfLine (text);
switch (text.getAndAdvance())
{
case 0: finished = true; break;
case '\n': break;
case '\r': if (*text == '\n') ++text; break;
default: continue;
}
strings.add (String (startOfLine, endOfLine));
++numLines;
break;
}
}
return numLines;
}
StringArray StringArray::fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings)
{
StringArray s;
s.addTokens (stringToTokenise, preserveQuotedStrings);
return s;
}
StringArray StringArray::fromTokens (StringRef stringToTokenise,
StringRef breakCharacters,
StringRef quoteCharacters)
{
StringArray s;
s.addTokens (stringToTokenise, breakCharacters, quoteCharacters);
return s;
}
StringArray StringArray::fromLines (StringRef stringToBreakUp)
{
StringArray s;
s.addLines (stringToBreakUp);
return s;
}
//==============================================================================
void StringArray::removeDuplicates (const bool ignoreCase)
{
for (int i = 0; i < size() - 1; ++i)
{
const String s (strings.getReference(i));
for (int nextIndex = i + 1;;)
{
nextIndex = indexOf (s, ignoreCase, nextIndex);
if (nextIndex < 0)
break;
strings.remove (nextIndex);
}
}
}
void StringArray::appendNumbersToDuplicates (const bool ignoreCase,
const bool appendNumberToFirstInstance,
CharPointer_UTF8 preNumberString,
CharPointer_UTF8 postNumberString)
{
CharPointer_UTF8 defaultPre (" ("), defaultPost (")");
if (preNumberString.getAddress() == nullptr)
preNumberString = defaultPre;
if (postNumberString.getAddress() == nullptr)
postNumberString = defaultPost;
for (int i = 0; i < size() - 1; ++i)
{
String& s = strings.getReference(i);
int nextIndex = indexOf (s, ignoreCase, i + 1);
if (nextIndex >= 0)
{
const String original (s);
int number = 0;
if (appendNumberToFirstInstance)
s = original + String (preNumberString) + String (++number) + String (postNumberString);
else
++number;
while (nextIndex >= 0)
{
set (nextIndex, (*this)[nextIndex] + String (preNumberString) + String (++number) + String (postNumberString));
nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
}
}
}
}
void StringArray::ensureStorageAllocated (int minNumElements)
{
strings.ensureStorageAllocated (minNumElements);
}
void StringArray::minimiseStorageOverheads()
{
strings.minimiseStorageOverheads();
}

+ 441
- 0
source/modules/juce_audio_graph/text/juce_StringArray.h View File

@@ -0,0 +1,441 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_STRINGARRAY_H_INCLUDED
#define JUCE_STRINGARRAY_H_INCLUDED
//==============================================================================
/**
A special array for holding a list of strings.
@see String, StringPairArray
*/
class JUCE_API StringArray
{
public:
//==============================================================================
/** Creates an empty string array */
StringArray() noexcept;
/** Creates a copy of another string array */
StringArray (const StringArray&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
StringArray (StringArray&&) noexcept;
#endif
/** Creates an array containing a single string. */
explicit StringArray (const String& firstValue);
/** Creates an array from a raw array of strings.
@param strings an array of strings to add
@param numberOfStrings how many items there are in the array
*/
StringArray (const String* strings, int numberOfStrings);
/** Creates a copy of an array of string literals.
@param strings an array of strings to add. Null pointers in the array will be
treated as empty strings
@param numberOfStrings how many items there are in the array
*/
StringArray (const char* const* strings, int numberOfStrings);
/** Creates a copy of a null-terminated array of string literals.
Each item from the array passed-in is added, until it encounters a null pointer,
at which point it stops.
*/
explicit StringArray (const char* const* strings);
/** Creates a copy of a null-terminated array of string literals.
Each item from the array passed-in is added, until it encounters a null pointer,
at which point it stops.
*/
explicit StringArray (const wchar_t* const* strings);
/** Creates a copy of an array of string literals.
@param strings an array of strings to add. Null pointers in the array will be
treated as empty strings
@param numberOfStrings how many items there are in the array
*/
StringArray (const wchar_t* const* strings, int numberOfStrings);
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
StringArray (const std::initializer_list<const char*>& strings);
#endif
/** Destructor. */
~StringArray();
/** Copies the contents of another string array into this one */
StringArray& operator= (const StringArray&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
StringArray& operator= (StringArray&&) noexcept;
#endif
/** Swaps the contents of this and another StringArray. */
void swapWith (StringArray&) noexcept;
//==============================================================================
/** Compares two arrays.
Comparisons are case-sensitive.
@returns true only if the other array contains exactly the same strings in the same order
*/
bool operator== (const StringArray&) const noexcept;
/** Compares two arrays.
Comparisons are case-sensitive.
@returns false if the other array contains exactly the same strings in the same order
*/
bool operator!= (const StringArray&) const noexcept;
//==============================================================================
/** Returns the number of strings in the array */
inline int size() const noexcept { return strings.size(); }
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept { return size() == 0; }
/** Returns one of the strings from the array.
If the index is out-of-range, an empty string is returned.
Obviously the reference returned shouldn't be stored for later use, as the
string it refers to may disappear when the array changes.
*/
const String& operator[] (int index) const noexcept;
/** Returns a reference to one of the strings in the array.
This lets you modify a string in-place in the array, but you must be sure that
the index is in-range.
*/
String& getReference (int index) noexcept;
/** Returns a pointer to the first String in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline String* begin() const noexcept { return strings.begin(); }
/** Returns a pointer to the String which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline String* end() const noexcept { return strings.end(); }
/** Searches for a string in the array.
The comparison will be case-insensitive if the ignoreCase parameter is true.
@returns true if the string is found inside the array
*/
bool contains (StringRef stringToLookFor,
bool ignoreCase = false) const;
/** Searches for a string in the array.
The comparison will be case-insensitive if the ignoreCase parameter is true.
@param stringToLookFor the string to try to find
@param ignoreCase whether the comparison should be case-insensitive
@param startIndex the first index to start searching from
@returns the index of the first occurrence of the string in this array,
or -1 if it isn't found.
*/
int indexOf (StringRef stringToLookFor,
bool ignoreCase = false,
int startIndex = 0) const;
//==============================================================================
/** Appends a string at the end of the array. */
void add (const String& stringToAdd);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
/** Appends a string at the end of the array. */
void add (String&& stringToAdd);
#endif
/** Inserts a string into the array.
This will insert a string into the array at the given index, moving
up the other elements to make room for it.
If the index is less than zero or greater than the size of the array,
the new string will be added to the end of the array.
*/
void insert (int index, const String& stringToAdd);
/** Adds a string to the array as long as it's not already in there.
The search can optionally be case-insensitive.
@return true if the string has been added, false otherwise.
*/
bool addIfNotAlreadyThere (const String& stringToAdd, bool ignoreCase = false);
/** Replaces one of the strings in the array with another one.
If the index is higher than the array's size, the new string will be
added to the end of the array; if it's less than zero nothing happens.
*/
void set (int index, const String& newString);
/** Appends some strings from another array to the end of this one.
@param other the array to add
@param startIndex the first element of the other array to add
@param numElementsToAdd the maximum number of elements to add (if this is
less than zero, they are all added)
*/
void addArray (const StringArray& other,
int startIndex = 0,
int numElementsToAdd = -1);
/** Merges the strings from another array into this one.
This will not add a string that already exists.
@param other the array to add
@param ignoreCase ignore case when merging
*/
void mergeArray (const StringArray& other,
bool ignoreCase = false);
/** Breaks up a string into tokens and adds them to this array.
This will tokenise the given string using whitespace characters as the
token delimiters, and will add these tokens to the end of the array.
@returns the number of tokens added
@see fromTokens
*/
int addTokens (StringRef stringToTokenise, bool preserveQuotedStrings);
/** Breaks up a string into tokens and adds them to this array.
This will tokenise the given string (using the string passed in to define the
token delimiters), and will add these tokens to the end of the array.
@param stringToTokenise the string to tokenise
@param breakCharacters a string of characters, any of which will be considered
to be a token delimiter.
@param quoteCharacters if this string isn't empty, it defines a set of characters
which are treated as quotes. Any text occurring
between quotes is not broken up into tokens.
@returns the number of tokens added
@see fromTokens
*/
int addTokens (StringRef stringToTokenise,
StringRef breakCharacters,
StringRef quoteCharacters);
/** Breaks up a string into lines and adds them to this array.
This breaks a string down into lines separated by \\n or \\r\\n, and adds each line
to the array. Line-break characters are omitted from the strings that are added to
the array.
*/
int addLines (StringRef stringToBreakUp);
/** Returns an array containing the tokens in a given string.
This will tokenise the given string using whitespace characters as the
token delimiters, and return the parsed tokens as an array.
@see addTokens
*/
static StringArray fromTokens (StringRef stringToTokenise,
bool preserveQuotedStrings);
/** Returns an array containing the tokens in a given string.
This will tokenise the given string using the breakCharacters string to define
the token delimiters, and will return the parsed tokens as an array.
@param stringToTokenise the string to tokenise
@param breakCharacters a string of characters, any of which will be considered
to be a token delimiter.
@param quoteCharacters if this string isn't empty, it defines a set of characters
which are treated as quotes. Any text occurring
between quotes is not broken up into tokens.
@see addTokens
*/
static StringArray fromTokens (StringRef stringToTokenise,
StringRef breakCharacters,
StringRef quoteCharacters);
/** Returns an array containing the lines in a given string.
This breaks a string down into lines separated by \\n or \\r\\n, and returns an
array containing these lines. Line-break characters are omitted from the strings that
are added to the array.
*/
static StringArray fromLines (StringRef stringToBreakUp);
//==============================================================================
/** Removes all elements from the array. */
void clear();
/** Removes all elements from the array without freeing the array's allocated storage.
@see clear
*/
void clearQuick();
/** Removes a string from the array.
If the index is out-of-range, no action will be taken.
*/
void remove (int index);
/** Finds a string in the array and removes it.
This will remove all occurrences of the given string from the array.
The comparison may be case-insensitive depending on the ignoreCase parameter.
*/
void removeString (StringRef stringToRemove,
bool ignoreCase = false);
/** Removes a range of elements from the array.
This will remove a set of elements, starting from the given index,
and move subsequent elements down to close the gap.
If the range extends beyond the bounds of the array, it will
be safely clipped to the size of the array.
@param startIndex the index of the first element to remove
@param numberToRemove how many elements should be removed
*/
void removeRange (int startIndex, int numberToRemove);
/** Removes any duplicated elements from the array.
If any string appears in the array more than once, only the first occurrence of
it will be retained.
@param ignoreCase whether to use a case-insensitive comparison
*/
void removeDuplicates (bool ignoreCase);
/** Removes empty strings from the array.
@param removeWhitespaceStrings if true, strings that only contain whitespace
characters will also be removed
*/
void removeEmptyStrings (bool removeWhitespaceStrings = true);
/** Moves one of the strings to a different position.
This will move the string to a specified index, shuffling along
any intervening elements as required.
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
@param currentIndex the index of the value to be moved. If this isn't a
valid index, then nothing will be done
@param newIndex the index at which you'd like this value to end up. If this
is less than zero, the value will be moved to the end
of the array
*/
void move (int currentIndex, int newIndex) noexcept;
/** Deletes any whitespace characters from the starts and ends of all the strings. */
void trim();
/** Adds numbers to the strings in the array, to make each string unique.
This will add numbers to the ends of groups of similar strings.
e.g. if there are two "moose" strings, they will become "moose (1)" and "moose (2)"
@param ignoreCaseWhenComparing whether the comparison used is case-insensitive
@param appendNumberToFirstInstance whether the first of a group of similar strings
also has a number appended to it.
@param preNumberString when adding a number, this string is added before the number.
If you pass nullptr, a default string will be used, which adds
brackets around the number.
@param postNumberString this string is appended after any numbers that are added.
If you pass nullptr, a default string will be used, which adds
brackets around the number.
*/
void appendNumbersToDuplicates (bool ignoreCaseWhenComparing,
bool appendNumberToFirstInstance,
CharPointer_UTF8 preNumberString = CharPointer_UTF8 (nullptr),
CharPointer_UTF8 postNumberString = CharPointer_UTF8 (nullptr));
//==============================================================================
/** Joins the strings in the array together into one string.
This will join a range of elements from the array into a string, separating
them with a given string.
e.g. joinIntoString (",") will turn an array of "a" "b" and "c" into "a,b,c".
@param separatorString the string to insert between all the strings
@param startIndex the first element to join
@param numberOfElements how many elements to join together. If this is less
than zero, all available elements will be used.
*/
String joinIntoString (StringRef separatorString,
int startIndex = 0,
int numberOfElements = -1) const;
//==============================================================================
/** Sorts the array into alphabetical order.
@param ignoreCase if true, the comparisons used will be case-sensitive.
*/
void sort (bool ignoreCase);
/** Sorts the array using extra language-aware rules to do a better job of comparing
words containing spaces and numbers.
@see String::compareNatural()
*/
void sortNatural();
//==============================================================================
/** Increases the array's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the array won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (int minNumElements);
/** Reduces the amount of storage being used by the array.
Arrays typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads();
/** This is the array holding the actual strings. This is public to allow direct access
to array methods that may not already be provided by the StringArray class.
*/
Array<String> strings;
private:
JUCE_LEAK_DETECTOR (StringArray)
};
#endif // JUCE_STRINGARRAY_H_INCLUDED

+ 168
- 0
source/modules/juce_audio_graph/text/juce_StringPool.cpp View File

@@ -0,0 +1,168 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
static const int minNumberOfStringsForGarbageCollection = 300;
static const uint32 garbageCollectionInterval = 30000;
StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {}
StringPool::~StringPool() {}
struct StartEndString
{
StartEndString (String::CharPointerType s, String::CharPointerType e) noexcept : start (s), end (e) {}
operator String() const { return String (start, end); }
String::CharPointerType start, end;
};
static int compareStrings (const String& s1, const String& s2) noexcept { return s1.compare (s2); }
static int compareStrings (CharPointer_UTF8 s1, const String& s2) noexcept { return s1.compare (s2.getCharPointer()); }
static int compareStrings (const StartEndString& string1, const String& string2) noexcept
{
String::CharPointerType s1 (string1.start), s2 (string2.getCharPointer());
for (;;)
{
const int c1 = s1 < string1.end ? (int) s1.getAndAdvance() : 0;
const int c2 = (int) s2.getAndAdvance();
const int diff = c1 - c2;
if (diff != 0) return diff < 0 ? -1 : 1;
if (c1 == 0) break;
}
return 0;
}
template <typename NewStringType>
static String addPooledString (Array<String>& strings, const NewStringType& newString)
{
int start = 0;
int end = strings.size();
while (start < end)
{
const String& startString = strings.getReference (start);
const int startComp = compareStrings (newString, startString);
if (startComp == 0)
return startString;
const int halfway = (start + end) / 2;
if (halfway == start)
{
if (startComp > 0)
++start;
break;
}
const String& halfwayString = strings.getReference (halfway);
const int halfwayComp = compareStrings (newString, halfwayString);
if (halfwayComp == 0)
return halfwayString;
if (halfwayComp > 0)
start = halfway;
else
end = halfway;
}
strings.insert (start, newString);
return strings.getReference (start);
}
String StringPool::getPooledString (const char* const newString)
{
if (newString == nullptr || *newString == 0)
return String();
const ScopedLock sl (lock);
garbageCollectIfNeeded();
return addPooledString (strings, CharPointer_UTF8 (newString));
}
String StringPool::getPooledString (String::CharPointerType start, String::CharPointerType end)
{
if (start.isEmpty() || start == end)
return String();
const ScopedLock sl (lock);
garbageCollectIfNeeded();
return addPooledString (strings, StartEndString (start, end));
}
String StringPool::getPooledString (StringRef newString)
{
if (newString.isEmpty())
return String();
const ScopedLock sl (lock);
garbageCollectIfNeeded();
return addPooledString (strings, newString.text);
}
String StringPool::getPooledString (const String& newString)
{
if (newString.isEmpty())
return String();
const ScopedLock sl (lock);
garbageCollectIfNeeded();
return addPooledString (strings, newString);
}
void StringPool::garbageCollectIfNeeded()
{
if (strings.size() > minNumberOfStringsForGarbageCollection
&& Time::getApproximateMillisecondCounter() > lastGarbageCollectionTime + garbageCollectionInterval)
garbageCollect();
}
void StringPool::garbageCollect()
{
const ScopedLock sl (lock);
for (int i = strings.size(); --i >= 0;)
if (strings.getReference(i).getReferenceCount() == 1)
strings.remove (i);
lastGarbageCollectionTime = Time::getApproximateMillisecondCounter();
}
StringPool& StringPool::getGlobalPool() noexcept
{
static StringPool pool;
return pool;
}

+ 98
- 0
source/modules/juce_audio_graph/text/juce_StringPool.h View File

@@ -0,0 +1,98 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_STRINGPOOL_H_INCLUDED
#define JUCE_STRINGPOOL_H_INCLUDED
//==============================================================================
/**
A StringPool holds a set of shared strings, which reduces storage overheads and improves
comparison speed when dealing with many duplicate strings.
When you add a string to a pool using getPooledString, it'll return a character
array containing the same string. This array is owned by the pool, and the same array
is returned every time a matching string is asked for. This means that it's trivial to
compare two pooled strings for equality, as you can simply compare their pointers. It
also cuts down on storage if you're using many copies of the same string.
*/
class JUCE_API StringPool
{
public:
//==============================================================================
/** Creates an empty pool. */
StringPool() noexcept;
/** Destructor */
~StringPool();
//==============================================================================
/** Returns a pointer to a shared copy of the string that is passed in.
The pool will always return the same String object when asked for a string that matches it.
*/
String getPooledString (const String& original);
/** Returns a pointer to a copy of the string that is passed in.
The pool will always return the same String object when asked for a string that matches it.
*/
String getPooledString (const char* original);
/** Returns a pointer to a shared copy of the string that is passed in.
The pool will always return the same String object when asked for a string that matches it.
*/
String getPooledString (StringRef original);
/** Returns a pointer to a copy of the string that is passed in.
The pool will always return the same String object when asked for a string that matches it.
*/
String getPooledString (String::CharPointerType start, String::CharPointerType end);
//==============================================================================
/** Scans the pool, and removes any strings that are unreferenced.
You don't generally need to call this - it'll be called automatically when the pool grows
large enough to warrant it.
*/
void garbageCollect();
/** Returns a shared global pool which is used for things like Identifiers, XML parsing. */
static StringPool& getGlobalPool() noexcept;
private:
Array<String> strings;
CarlaRecursiveMutex lock;
uint32 lastGarbageCollectionTime;
void garbageCollectIfNeeded();
JUCE_DECLARE_NON_COPYABLE (StringPool)
};
#endif // JUCE_STRINGPOOL_H_INCLUDED

+ 895
- 0
source/modules/juce_audio_graph/xml/juce_XmlDocument.cpp View File

@@ -0,0 +1,895 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
XmlDocument::XmlDocument (const String& documentText)
: originalText (documentText),
input (nullptr),
outOfData (false),
errorOccurred (false),
needToLoadDTD (false),
ignoreEmptyTextElements (true)
{
}
XmlDocument::XmlDocument (const File& file)
: input (nullptr),
outOfData (false),
errorOccurred (false),
needToLoadDTD (false),
ignoreEmptyTextElements (true),
inputSource (new FileInputSource (file))
{
}
XmlDocument::~XmlDocument()
{
}
XmlElement* XmlDocument::parse (const File& file)
{
XmlDocument doc (file);
return doc.getDocumentElement();
}
XmlElement* XmlDocument::parse (const String& xmlData)
{
XmlDocument doc (xmlData);
return doc.getDocumentElement();
}
void XmlDocument::setInputSource (InputSource* const newSource) noexcept
{
inputSource = newSource;
}
void XmlDocument::setEmptyTextElementsIgnored (const bool shouldBeIgnored) noexcept
{
ignoreEmptyTextElements = shouldBeIgnored;
}
namespace XmlIdentifierChars
{
static bool isIdentifierCharSlow (const juce_wchar c) noexcept
{
return CharacterFunctions::isLetterOrDigit (c)
|| c == '_' || c == '-' || c == ':' || c == '.';
}
static bool isIdentifierChar (const juce_wchar c) noexcept
{
static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 };
return ((int) c < (int) numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (1 << (c & 31))) != 0)
: isIdentifierCharSlow (c);
}
/*static void generateIdentifierCharConstants()
{
uint32 n[8] = { 0 };
for (int i = 0; i < 256; ++i)
if (isIdentifierCharSlow (i))
n[i >> 5] |= (1 << (i & 31));
String s;
for (int i = 0; i < 8; ++i)
s << "0x" << String::toHexString ((int) n[i]) << ", ";
DBG (s);
}*/
static String::CharPointerType findEndOfToken (String::CharPointerType p)
{
while (isIdentifierChar (*p))
++p;
return p;
}
}
XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement)
{
if (originalText.isEmpty() && inputSource != nullptr)
{
ScopedPointer<InputStream> in (inputSource->createInputStream());
if (in != nullptr)
{
MemoryOutputStream data;
data.writeFromInputStream (*in, onlyReadOuterDocumentElement ? 8192 : -1);
#if JUCE_STRING_UTF_TYPE == 8
if (data.getDataSize() > 2)
{
data.writeByte (0);
const char* text = static_cast<const char*> (data.getData());
if (CharPointer_UTF16::isByteOrderMarkBigEndian (text)
|| CharPointer_UTF16::isByteOrderMarkLittleEndian (text))
{
originalText = data.toString();
}
else
{
if (CharPointer_UTF8::isByteOrderMark (text))
text += 3;
// parse the input buffer directly to avoid copying it all to a string..
return parseDocumentElement (String::CharPointerType (text), onlyReadOuterDocumentElement);
}
}
#else
originalText = data.toString();
#endif
}
}
return parseDocumentElement (originalText.getCharPointer(), onlyReadOuterDocumentElement);
}
const String& XmlDocument::getLastParseError() const noexcept
{
return lastError;
}
void XmlDocument::setLastError (const String& desc, const bool carryOn)
{
lastError = desc;
errorOccurred = ! carryOn;
}
String XmlDocument::getFileContents (const String& filename) const
{
if (inputSource != nullptr)
{
const ScopedPointer<InputStream> in (inputSource->createInputStreamFor (filename.trim().unquoted()));
if (in != nullptr)
return in->readEntireStreamAsString();
}
return String();
}
juce_wchar XmlDocument::readNextChar() noexcept
{
const juce_wchar c = input.getAndAdvance();
if (c == 0)
{
outOfData = true;
--input;
}
return c;
}
XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToParse,
const bool onlyReadOuterDocumentElement)
{
input = textToParse;
errorOccurred = false;
outOfData = false;
needToLoadDTD = true;
if (textToParse.isEmpty())
{
lastError = "not enough input";
}
else if (! parseHeader())
{
lastError = "malformed header";
}
else if (! parseDTD())
{
lastError = "malformed DTD";
}
else
{
lastError.clear();
ScopedPointer<XmlElement> result (readNextElement (! onlyReadOuterDocumentElement));
if (! errorOccurred)
return result.release();
}
return nullptr;
}
bool XmlDocument::parseHeader()
{
skipNextWhiteSpace();
if (CharacterFunctions::compareUpTo (input, CharPointer_UTF8 ("<?xml"), 5) == 0)
{
const String::CharPointerType headerEnd (CharacterFunctions::find (input, CharPointer_UTF8 ("?>")));
if (headerEnd.isEmpty())
return false;
#if JUCE_DEBUG
const String encoding (String (input, headerEnd)
.fromFirstOccurrenceOf ("encoding", false, true)
.fromFirstOccurrenceOf ("=", false, false)
.fromFirstOccurrenceOf ("\"", false, false)
.upToFirstOccurrenceOf ("\"", false, false).trim());
/* If you load an XML document with a non-UTF encoding type, it may have been
loaded wrongly.. Since all the files are read via the normal juce file streams,
they're treated as UTF-8, so by the time it gets to the parser, the encoding will
have been lost. Best plan is to stick to utf-8 or if you have specific files to
read, use your own code to convert them to a unicode String, and pass that to the
XML parser.
*/
jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-"));
#endif
input = headerEnd + 2;
skipNextWhiteSpace();
}
return true;
}
bool XmlDocument::parseDTD()
{
if (CharacterFunctions::compareUpTo (input, CharPointer_UTF8 ("<!DOCTYPE"), 9) == 0)
{
input += 9;
const String::CharPointerType dtdStart (input);
for (int n = 1; n > 0;)
{
const juce_wchar c = readNextChar();
if (outOfData)
return false;
if (c == '<')
++n;
else if (c == '>')
--n;
}
dtdText = String (dtdStart, input - 1).trim();
}
return true;
}
void XmlDocument::skipNextWhiteSpace()
{
for (;;)
{
input = input.findEndOfWhitespace();
if (input.isEmpty())
{
outOfData = true;
break;
}
if (*input == '<')
{
if (input[1] == '!'
&& input[2] == '-'
&& input[3] == '-')
{
input += 4;
const int closeComment = input.indexOf (CharPointer_UTF8 ("-->"));
if (closeComment < 0)
{
outOfData = true;
break;
}
input += closeComment + 3;
continue;
}
if (input[1] == '?')
{
input += 2;
const int closeBracket = input.indexOf (CharPointer_UTF8 ("?>"));
if (closeBracket < 0)
{
outOfData = true;
break;
}
input += closeBracket + 2;
continue;
}
}
break;
}
}
void XmlDocument::readQuotedString (String& result)
{
const juce_wchar quote = readNextChar();
while (! outOfData)
{
const juce_wchar c = readNextChar();
if (c == quote)
break;
--input;
if (c == '&')
{
readEntity (result);
}
else
{
const String::CharPointerType start (input);
for (;;)
{
const juce_wchar character = *input;
if (character == quote)
{
result.appendCharPointer (start, input);
++input;
return;
}
else if (character == '&')
{
result.appendCharPointer (start, input);
break;
}
else if (character == 0)
{
setLastError ("unmatched quotes", false);
outOfData = true;
break;
}
++input;
}
}
}
}
XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements)
{
XmlElement* node = nullptr;
skipNextWhiteSpace();
if (outOfData)
return nullptr;
if (*input == '<')
{
++input;
String::CharPointerType endOfToken (XmlIdentifierChars::findEndOfToken (input));
if (endOfToken == input)
{
// no tag name - but allow for a gap after the '<' before giving an error
skipNextWhiteSpace();
endOfToken = XmlIdentifierChars::findEndOfToken (input);
if (endOfToken == input)
{
setLastError ("tag name missing", false);
return node;
}
}
node = new XmlElement (input, endOfToken);
input = endOfToken;
LinkedListPointer<XmlElement::XmlAttributeNode>::Appender attributeAppender (node->attributes);
// look for attributes
for (;;)
{
skipNextWhiteSpace();
const juce_wchar c = *input;
// empty tag..
if (c == '/' && input[1] == '>')
{
input += 2;
break;
}
// parse the guts of the element..
if (c == '>')
{
++input;
if (alsoParseSubElements)
readChildElements (*node);
break;
}
// get an attribute..
if (XmlIdentifierChars::isIdentifierChar (c))
{
String::CharPointerType attNameEnd (XmlIdentifierChars::findEndOfToken (input));
if (attNameEnd != input)
{
const String::CharPointerType attNameStart (input);
input = attNameEnd;
skipNextWhiteSpace();
if (readNextChar() == '=')
{
skipNextWhiteSpace();
const juce_wchar nextChar = *input;
if (nextChar == '"' || nextChar == '\'')
{
XmlElement::XmlAttributeNode* const newAtt
= new XmlElement::XmlAttributeNode (attNameStart, attNameEnd);
readQuotedString (newAtt->value);
attributeAppender.append (newAtt);
continue;
}
}
else
{
setLastError ("expected '=' after attribute '"
+ String (attNameStart, attNameEnd) + "'", false);
return node;
}
}
}
else
{
if (! outOfData)
setLastError ("illegal character found in " + node->getTagName() + ": '" + c + "'", false);
}
break;
}
}
return node;
}
void XmlDocument::readChildElements (XmlElement& parent)
{
LinkedListPointer<XmlElement>::Appender childAppender (parent.firstChildElement);
for (;;)
{
const String::CharPointerType preWhitespaceInput (input);
skipNextWhiteSpace();
if (outOfData)
{
setLastError ("unmatched tags", false);
break;
}
if (*input == '<')
{
const juce_wchar c1 = input[1];
if (c1 == '/')
{
// our close tag..
const int closeTag = input.indexOf ((juce_wchar) '>');
if (closeTag >= 0)
input += closeTag + 1;
break;
}
if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_UTF8 ("[CDATA["), 7) == 0)
{
input += 9;
const String::CharPointerType inputStart (input);
for (;;)
{
const juce_wchar c0 = *input;
if (c0 == 0)
{
setLastError ("unterminated CDATA section", false);
outOfData = true;
break;
}
else if (c0 == ']'
&& input[1] == ']'
&& input[2] == '>')
{
childAppender.append (XmlElement::createTextElement (String (inputStart, input)));
input += 3;
break;
}
++input;
}
}
else
{
// this is some other element, so parse and add it..
if (XmlElement* const n = readNextElement (true))
childAppender.append (n);
else
break;
}
}
else // must be a character block
{
input = preWhitespaceInput; // roll back to include the leading whitespace
MemoryOutputStream textElementContent;
bool contentShouldBeUsed = ! ignoreEmptyTextElements;
for (;;)
{
const juce_wchar c = *input;
if (c == '<')
{
if (input[1] == '!' && input[2] == '-' && input[3] == '-')
{
input += 4;
const int closeComment = input.indexOf (CharPointer_UTF8 ("-->"));
if (closeComment < 0)
{
setLastError ("unterminated comment", false);
outOfData = true;
return;
}
input += closeComment + 3;
continue;
}
break;
}
if (c == 0)
{
setLastError ("unmatched tags", false);
outOfData = true;
return;
}
if (c == '&')
{
String entity;
readEntity (entity);
if (entity.startsWithChar ('<') && entity [1] != 0)
{
const String::CharPointerType oldInput (input);
const bool oldOutOfData = outOfData;
input = entity.getCharPointer();
outOfData = false;
while (XmlElement* n = readNextElement (true))
childAppender.append (n);
input = oldInput;
outOfData = oldOutOfData;
}
else
{
textElementContent << entity;
contentShouldBeUsed = contentShouldBeUsed || entity.containsNonWhitespaceChars();
}
}
else
{
for (;; ++input)
{
juce_wchar nextChar = *input;
if (nextChar == '\r')
{
nextChar = '\n';
if (input[1] == '\n')
continue;
}
if (nextChar == '<' || nextChar == '&')
break;
if (nextChar == 0)
{
setLastError ("unmatched tags", false);
outOfData = true;
return;
}
textElementContent.appendUTF8Char (nextChar);
contentShouldBeUsed = contentShouldBeUsed || ! CharacterFunctions::isWhitespace (nextChar);
}
}
}
if (contentShouldBeUsed)
childAppender.append (XmlElement::createTextElement (textElementContent.toUTF8()));
}
}
}
void XmlDocument::readEntity (String& result)
{
// skip over the ampersand
++input;
if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("amp;"), 4) == 0)
{
input += 4;
result += '&';
}
else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("quot;"), 5) == 0)
{
input += 5;
result += '"';
}
else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("apos;"), 5) == 0)
{
input += 5;
result += '\'';
}
else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("lt;"), 3) == 0)
{
input += 3;
result += '<';
}
else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("gt;"), 3) == 0)
{
input += 3;
result += '>';
}
else if (*input == '#')
{
int charCode = 0;
++input;
if (*input == 'x' || *input == 'X')
{
++input;
int numChars = 0;
while (input[0] != ';')
{
const int hexValue = CharacterFunctions::getHexDigitValue (input[0]);
if (hexValue < 0 || ++numChars > 8)
{
setLastError ("illegal escape sequence", true);
break;
}
charCode = (charCode << 4) | hexValue;
++input;
}
++input;
}
else if (input[0] >= '0' && input[0] <= '9')
{
int numChars = 0;
while (input[0] != ';')
{
if (++numChars > 12)
{
setLastError ("illegal escape sequence", true);
break;
}
charCode = charCode * 10 + ((int) input[0] - '0');
++input;
}
++input;
}
else
{
setLastError ("illegal escape sequence", true);
result += '&';
return;
}
#if 0 // FIXME
result << (juce_wchar) charCode;
#endif
}
else
{
const String::CharPointerType entityNameStart (input);
const int closingSemiColon = input.indexOf ((juce_wchar) ';');
if (closingSemiColon < 0)
{
outOfData = true;
result += '&';
}
else
{
input += closingSemiColon + 1;
result += expandExternalEntity (String (entityNameStart, (size_t) closingSemiColon));
}
}
}
String XmlDocument::expandEntity (const String& ent)
{
if (ent.equalsIgnoreCase ("amp")) return String::charToString ('&');
if (ent.equalsIgnoreCase ("quot")) return String::charToString ('"');
if (ent.equalsIgnoreCase ("apos")) return String::charToString ('\'');
if (ent.equalsIgnoreCase ("lt")) return String::charToString ('<');
if (ent.equalsIgnoreCase ("gt")) return String::charToString ('>');
if (ent[0] == '#')
{
const juce_wchar char1 = ent[1];
if (char1 == 'x' || char1 == 'X')
return String::charToString (static_cast<juce_wchar> (ent.substring (2).getHexValue32()));
if (char1 >= '0' && char1 <= '9')
return String::charToString (static_cast<juce_wchar> (ent.substring (1).getIntValue()));
setLastError ("illegal escape sequence", false);
return String::charToString ('&');
}
return expandExternalEntity (ent);
}
String XmlDocument::expandExternalEntity (const String& entity)
{
if (needToLoadDTD)
{
if (dtdText.isNotEmpty())
{
dtdText = dtdText.trimCharactersAtEnd (">");
tokenisedDTD.addTokens (dtdText, true);
if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase ("system")
&& tokenisedDTD [tokenisedDTD.size() - 1].isQuotedString())
{
const String fn (tokenisedDTD [tokenisedDTD.size() - 1]);
tokenisedDTD.clear();
tokenisedDTD.addTokens (getFileContents (fn), true);
}
else
{
tokenisedDTD.clear();
const int openBracket = dtdText.indexOfChar ('[');
if (openBracket > 0)
{
const int closeBracket = dtdText.lastIndexOfChar (']');
if (closeBracket > openBracket)
tokenisedDTD.addTokens (dtdText.substring (openBracket + 1,
closeBracket), true);
}
}
for (int i = tokenisedDTD.size(); --i >= 0;)
{
if (tokenisedDTD[i].startsWithChar ('%')
&& tokenisedDTD[i].endsWithChar (';'))
{
const String parsed (getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1)));
StringArray newToks;
newToks.addTokens (parsed, true);
tokenisedDTD.remove (i);
for (int j = newToks.size(); --j >= 0;)
tokenisedDTD.insert (i, newToks[j]);
}
}
}
needToLoadDTD = false;
}
for (int i = 0; i < tokenisedDTD.size(); ++i)
{
if (tokenisedDTD[i] == entity)
{
if (tokenisedDTD[i - 1].equalsIgnoreCase ("<!entity"))
{
String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted());
// check for sub-entities..
int ampersand = ent.indexOfChar ('&');
while (ampersand >= 0)
{
const int semiColon = ent.indexOf (i + 1, ";");
if (semiColon < 0)
{
setLastError ("entity without terminating semi-colon", false);
break;
}
const String resolved (expandEntity (ent.substring (i + 1, semiColon)));
ent = ent.substring (0, ampersand)
+ resolved
+ ent.substring (semiColon + 1);
ampersand = ent.indexOfChar (semiColon + 1, '&');
}
return ent;
}
}
}
setLastError ("unknown entity", true);
return entity;
}
String XmlDocument::getParameterEntity (const String& entity)
{
for (int i = 0; i < tokenisedDTD.size(); ++i)
{
if (tokenisedDTD[i] == entity
&& tokenisedDTD [i - 1] == "%"
&& tokenisedDTD [i - 2].equalsIgnoreCase ("<!entity"))
{
const String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">"));
if (ent.equalsIgnoreCase ("system"))
return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">"));
return ent.trim().unquoted();
}
}
return entity;
}

+ 183
- 0
source/modules/juce_audio_graph/xml/juce_XmlDocument.h View File

@@ -0,0 +1,183 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_XMLDOCUMENT_H_INCLUDED
#define JUCE_XMLDOCUMENT_H_INCLUDED
//==============================================================================
/**
Parses a text-based XML document and creates an XmlElement object from it.
The parser will parse DTDs to load external entities but won't
check the document for validity against the DTD.
e.g.
@code
XmlDocument myDocument (File ("myfile.xml"));
ScopedPointer<XmlElement> mainElement (myDocument.getDocumentElement());
if (mainElement == nullptr)
{
String error = myDocument.getLastParseError();
}
else
{
..use the element
}
@endcode
Or you can use the static helper methods for quick parsing..
@code
ScopedPointer<XmlElement> xml (XmlDocument::parse (myXmlFile));
if (xml != nullptr && xml->hasTagName ("foobar"))
{
...etc
@endcode
@see XmlElement
*/
class JUCE_API XmlDocument
{
public:
//==============================================================================
/** Creates an XmlDocument from the xml text.
The text doesn't actually get parsed until the getDocumentElement() method is called.
*/
XmlDocument (const String& documentText);
/** Creates an XmlDocument from a file.
The text doesn't actually get parsed until the getDocumentElement() method is called.
*/
XmlDocument (const File& file);
/** Destructor. */
~XmlDocument();
//==============================================================================
/** Creates an XmlElement object to represent the main document node.
This method will do the actual parsing of the text, and if there's a
parse error, it may returns nullptr (and you can find out the error using
the getLastParseError() method).
See also the parse() methods, which provide a shorthand way to quickly
parse a file or string.
@param onlyReadOuterDocumentElement if true, the parser will only read the
first section of the file, and will only
return the outer document element - this
allows quick checking of large files to
see if they contain the correct type of
tag, without having to parse the entire file
@returns a new XmlElement which the caller will need to delete, or null if
there was an error.
@see getLastParseError
*/
XmlElement* getDocumentElement (bool onlyReadOuterDocumentElement = false);
/** Returns the parsing error that occurred the last time getDocumentElement was called.
@returns the error, or an empty string if there was no error.
*/
const String& getLastParseError() const noexcept;
/** Sets an input source object to use for parsing documents that reference external entities.
If the document has been created from a file, this probably won't be needed, but
if you're parsing some text and there might be a DTD that references external
files, you may need to create a custom input source that can retrieve the
other files it needs.
The object that is passed-in will be deleted automatically when no longer needed.
@see InputSource
*/
void setInputSource (InputSource* newSource) noexcept;
/** Sets a flag to change the treatment of empty text elements.
If this is true (the default state), then any text elements that contain only
whitespace characters will be ingored during parsing. If you need to catch
whitespace-only text, then you should set this to false before calling the
getDocumentElement() method.
*/
void setEmptyTextElementsIgnored (bool shouldBeIgnored) noexcept;
//==============================================================================
/** A handy static method that parses a file.
This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it.
@returns a new XmlElement which the caller will need to delete, or null if there was an error.
*/
static XmlElement* parse (const File& file);
/** A handy static method that parses some XML data.
This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it.
@returns a new XmlElement which the caller will need to delete, or null if there was an error.
*/
static XmlElement* parse (const String& xmlData);
//==============================================================================
private:
String originalText;
String::CharPointerType input;
bool outOfData, errorOccurred;
String lastError, dtdText;
StringArray tokenisedDTD;
bool needToLoadDTD, ignoreEmptyTextElements;
ScopedPointer<InputSource> inputSource;
XmlElement* parseDocumentElement (String::CharPointerType, bool outer);
void setLastError (const String&, bool carryOn);
bool parseHeader();
bool parseDTD();
void skipNextWhiteSpace();
juce_wchar readNextChar() noexcept;
XmlElement* readNextElement (bool alsoParseSubElements);
void readChildElements (XmlElement&);
void readQuotedString (String&);
void readEntity (String&);
String getFileContents (const String&) const;
String expandEntity (const String&);
String expandExternalEntity (const String&);
String getParameterEntity (const String&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlDocument)
};
#endif // JUCE_XMLDOCUMENT_H_INCLUDED

+ 942
- 0
source/modules/juce_audio_graph/xml/juce_XmlElement.cpp View File

@@ -0,0 +1,942 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
namespace
{
inline bool isValidXmlNameStartCharacter (const juce_wchar character) noexcept
{
return character == ':'
|| character == '_'
|| (character >= 'a' && character <= 'z')
|| (character >= 'A' && character <= 'Z')
|| (character >= 0xc0 && character <= 0xd6)
|| (character >= 0xd8 && character <= 0xf6)
|| (character >= 0xf8 && character <= 0x2ff)
|| (character >= 0x370 && character <= 0x37d)
|| (character >= 0x37f && character <= 0x1fff)
|| (character >= 0x200c && character <= 0x200d)
|| (character >= 0x2070 && character <= 0x218f)
|| (character >= 0x2c00 && character <= 0x2fef)
|| (character >= 0x3001 && character <= 0xd7ff)
|| (character >= 0xf900 && character <= 0xfdcf)
|| (character >= 0xfdf0 && character <= 0xfffd)
|| (character >= 0x10000 && character <= 0xeffff);
}
inline bool isValidXmlNameBodyCharacter (const juce_wchar character) noexcept
{
return isValidXmlNameStartCharacter (character)
|| character == '-'
|| character == '.'
|| character == 0xb7
|| (character >= '0' && character <= '9')
|| (character >= 0x300 && character <= 0x036f)
|| (character >= 0x203f && character <= 0x2040);
}
}
XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
: name (other.name),
value (other.value)
{
}
XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept
: name (n), value (v)
{
jassert (isValidXmlName (name));
}
XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd)
: name (nameStart, nameEnd)
{
jassert (isValidXmlName (name));
}
//==============================================================================
XmlElement::XmlElement (const String& tag)
: tagName (StringPool::getGlobalPool().getPooledString (tag))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (const char* tag)
: tagName (StringPool::getGlobalPool().getPooledString (tag))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (StringRef tag)
: tagName (StringPool::getGlobalPool().getPooledString (tag))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (const Identifier& tag)
: tagName (tag.toString())
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd)
: tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (int /*dummy*/) noexcept
{
}
XmlElement::XmlElement (const XmlElement& other)
: tagName (other.tagName)
{
copyChildrenAndAttributesFrom (other);
}
XmlElement& XmlElement::operator= (const XmlElement& other)
{
if (this != &other)
{
removeAllAttributes();
deleteAllChildElements();
tagName = other.tagName;
copyChildrenAndAttributesFrom (other);
}
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
XmlElement::XmlElement (XmlElement&& other) noexcept
: nextListItem (static_cast<LinkedListPointer<XmlElement>&&> (other.nextListItem)),
firstChildElement (static_cast<LinkedListPointer<XmlElement>&&> (other.firstChildElement)),
attributes (static_cast<LinkedListPointer<XmlAttributeNode>&&> (other.attributes)),
tagName (static_cast<String&&> (other.tagName))
{
}
XmlElement& XmlElement::operator= (XmlElement&& other) noexcept
{
jassert (this != &other); // hopefully the compiler should make this situation impossible!
removeAllAttributes();
deleteAllChildElements();
nextListItem = static_cast<LinkedListPointer<XmlElement>&&> (other.nextListItem);
firstChildElement = static_cast<LinkedListPointer<XmlElement>&&> (other.firstChildElement);
attributes = static_cast<LinkedListPointer<XmlAttributeNode>&&> (other.attributes);
tagName = static_cast<String&&> (other.tagName);
return *this;
}
#endif
void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
{
jassert (firstChildElement.get() == nullptr);
firstChildElement.addCopyOfList (other.firstChildElement);
jassert (attributes.get() == nullptr);
attributes.addCopyOfList (other.attributes);
}
XmlElement::~XmlElement() noexcept
{
firstChildElement.deleteAll();
attributes.deleteAll();
}
//==============================================================================
namespace XmlOutputFunctions
{
#if 0 // (These functions are just used to generate the lookup table used below)
bool isLegalXmlCharSlow (const juce_wchar character) noexcept
{
if ((character >= 'a' && character <= 'z')
|| (character >= 'A' && character <= 'Z')
|| (character >= '0' && character <= '9'))
return true;
const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
do
{
if (((juce_wchar) (uint8) *t) == character)
return true;
}
while (*++t != 0);
return false;
}
void generateLegalCharLookupTable()
{
uint8 n[32] = { 0 };
for (int i = 0; i < 256; ++i)
if (isLegalXmlCharSlow (i))
n[i >> 3] |= (1 << (i & 7));
String s;
for (int i = 0; i < 32; ++i)
s << (int) n[i] << ", ";
DBG (s);
}
#endif
static bool isLegalXmlChar (const uint32 c) noexcept
{
static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255,
255, 255, 191, 254, 255, 255, 127 };
return c < sizeof (legalChars) * 8
&& (legalChars [c >> 3] & (1 << (c & 7))) != 0;
}
static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines)
{
String::CharPointerType t (text.getCharPointer());
for (;;)
{
const uint32 character = (uint32) t.getAndAdvance();
if (character == 0)
break;
if (isLegalXmlChar (character))
{
outputStream << (char) character;
}
else
{
switch (character)
{
case '&': outputStream << "&amp;"; break;
case '"': outputStream << "&quot;"; break;
case '>': outputStream << "&gt;"; break;
case '<': outputStream << "&lt;"; break;
case '\n':
case '\r':
if (! changeNewLines)
{
outputStream << (char) character;
break;
}
// Note: deliberate fall-through here!
default:
outputStream << "&#" << ((int) character) << ';';
break;
}
}
}
}
static void writeSpaces (OutputStream& out, const size_t numSpaces)
{
out.writeRepeatedByte (' ', numSpaces);
}
}
void XmlElement::writeElementAsText (OutputStream& outputStream,
const int indentationLevel,
const int lineWrapLength) const
{
using namespace XmlOutputFunctions;
if (indentationLevel >= 0)
writeSpaces (outputStream, (size_t) indentationLevel);
if (! isTextElement())
{
outputStream.writeByte ('<');
outputStream << tagName;
{
const size_t attIndent = (size_t) (indentationLevel + tagName.length() + 1);
int lineLen = 0;
for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
{
if (lineLen > lineWrapLength && indentationLevel >= 0)
{
outputStream << newLine;
writeSpaces (outputStream, attIndent);
lineLen = 0;
}
const int64 startPos = outputStream.getPosition();
outputStream.writeByte (' ');
outputStream << att->name;
outputStream.write ("=\"", 2);
escapeIllegalXmlChars (outputStream, att->value, true);
outputStream.writeByte ('"');
lineLen += (int) (outputStream.getPosition() - startPos);
}
}
if (firstChildElement != nullptr)
{
outputStream.writeByte ('>');
bool lastWasTextNode = false;
for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
{
if (child->isTextElement())
{
escapeIllegalXmlChars (outputStream, child->getText(), false);
lastWasTextNode = true;
}
else
{
if (indentationLevel >= 0 && ! lastWasTextNode)
outputStream << newLine;
child->writeElementAsText (outputStream,
lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength);
lastWasTextNode = false;
}
}
if (indentationLevel >= 0 && ! lastWasTextNode)
{
outputStream << newLine;
writeSpaces (outputStream, (size_t) indentationLevel);
}
outputStream.write ("</", 2);
outputStream << tagName;
outputStream.writeByte ('>');
}
else
{
outputStream.write ("/>", 2);
}
}
else
{
escapeIllegalXmlChars (outputStream, getText(), false);
}
}
String XmlElement::createDocument (StringRef dtdToUse,
const bool allOnOneLine,
const bool includeXmlHeader,
StringRef encodingType,
const int lineWrapLength) const
{
MemoryOutputStream mem (2048);
writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
return mem.toUTF8();
}
void XmlElement::writeToStream (OutputStream& output,
StringRef dtdToUse,
const bool allOnOneLine,
const bool includeXmlHeader,
StringRef encodingType,
const int lineWrapLength) const
{
using namespace XmlOutputFunctions;
if (includeXmlHeader)
{
output << "<?xml version=\"1.0\" encoding=\"" << encodingType << "\"?>";
if (allOnOneLine)
output.writeByte (' ');
else
output << newLine << newLine;
}
if (dtdToUse.isNotEmpty())
{
output << dtdToUse;
if (allOnOneLine)
output.writeByte (' ');
else
output << newLine;
}
writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
if (! allOnOneLine)
output << newLine;
}
#if 0
bool XmlElement::writeToFile (const File& file,
StringRef dtdToUse,
StringRef encodingType,
const int lineWrapLength) const
{
TemporaryFile tempFile (file);
{
FileOutputStream out (tempFile.getFile());
if (! out.openedOk())
return false;
writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength);
out.flush(); // (called explicitly to force an fsync on posix)
if (out.getStatus().failed())
return false;
}
return tempFile.overwriteTargetFileWithTemporary();
}
#endif
//==============================================================================
bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept
{
const bool matches = tagName.equalsIgnoreCase (possibleTagName);
// XML tags should be case-sensitive, so although this method allows a
// case-insensitive match to pass, you should try to avoid this.
jassert ((! matches) || tagName == possibleTagName);
return matches;
}
String XmlElement::getNamespace() const
{
return tagName.upToFirstOccurrenceOf (":", false, false);
}
String XmlElement::getTagNameWithoutNamespace() const
{
return tagName.fromLastOccurrenceOf (":", false, false);
}
bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const
{
return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName;
}
XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const
{
XmlElement* e = nextListItem;
while (e != nullptr && ! e->hasTagName (requiredTagName))
e = e->nextListItem;
return e;
}
//==============================================================================
int XmlElement::getNumAttributes() const noexcept
{
return attributes.size();
}
static const String& getEmptyStringRef() noexcept
{
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
return String::empty;
#else
static String empty;
return empty;
#endif
}
const String& XmlElement::getAttributeName (const int index) const noexcept
{
if (const XmlAttributeNode* const att = attributes [index])
return att->name.toString();
return getEmptyStringRef();
}
const String& XmlElement::getAttributeValue (const int index) const noexcept
{
if (const XmlAttributeNode* const att = attributes [index])
return att->value;
return getEmptyStringRef();
}
XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept
{
for (XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
if (att->name == attributeName)
return att;
return nullptr;
}
bool XmlElement::hasAttribute (StringRef attributeName) const noexcept
{
return getAttribute (attributeName) != nullptr;
}
//==============================================================================
const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept
{
if (const XmlAttributeNode* att = getAttribute (attributeName))
return att->value;
return getEmptyStringRef();
}
String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const
{
if (const XmlAttributeNode* att = getAttribute (attributeName))
return att->value;
return defaultReturnValue;
}
int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const
{
if (const XmlAttributeNode* att = getAttribute (attributeName))
return att->value.getIntValue();
return defaultReturnValue;
}
double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const
{
if (const XmlAttributeNode* att = getAttribute (attributeName))
return att->value.getDoubleValue();
return defaultReturnValue;
}
bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const
{
if (const XmlAttributeNode* att = getAttribute (attributeName))
{
const juce_wchar firstChar = *(att->value.getCharPointer().findEndOfWhitespace());
return firstChar == '1'
|| firstChar == 't'
|| firstChar == 'y'
|| firstChar == 'T'
|| firstChar == 'Y';
}
return defaultReturnValue;
}
bool XmlElement::compareAttribute (StringRef attributeName,
StringRef stringToCompareAgainst,
const bool ignoreCase) const noexcept
{
if (const XmlAttributeNode* att = getAttribute (attributeName))
return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
: att->value == stringToCompareAgainst;
return false;
}
//==============================================================================
void XmlElement::setAttribute (const Identifier& attributeName, const String& value)
{
if (attributes == nullptr)
{
attributes = new XmlAttributeNode (attributeName, value);
}
else
{
for (XmlAttributeNode* att = attributes; ; att = att->nextListItem)
{
if (att->name == attributeName)
{
att->value = value;
break;
}
if (att->nextListItem == nullptr)
{
att->nextListItem = new XmlAttributeNode (attributeName, value);
break;
}
}
}
}
void XmlElement::setAttribute (const Identifier& attributeName, const int number)
{
setAttribute (attributeName, String (number));
}
void XmlElement::setAttribute (const Identifier& attributeName, const double number)
{
setAttribute (attributeName, String (number, 20));
}
void XmlElement::removeAttribute (const Identifier& attributeName) noexcept
{
for (LinkedListPointer<XmlAttributeNode>* att = &attributes;
att->get() != nullptr;
att = &(att->get()->nextListItem))
{
if (att->get()->name == attributeName)
{
delete att->removeNext();
break;
}
}
}
void XmlElement::removeAllAttributes() noexcept
{
attributes.deleteAll();
}
//==============================================================================
int XmlElement::getNumChildElements() const noexcept
{
return firstChildElement.size();
}
XmlElement* XmlElement::getChildElement (const int index) const noexcept
{
return firstChildElement [index].get();
}
XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept
{
jassert (! childName.isEmpty());
for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
if (child->hasTagName (childName))
return child;
return nullptr;
}
XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept
{
jassert (! attributeName.isEmpty());
for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
if (child->compareAttribute (attributeName, attributeValue))
return child;
return nullptr;
}
void XmlElement::addChildElement (XmlElement* const newNode) noexcept
{
if (newNode != nullptr)
{
// The element being added must not be a child of another node!
jassert (newNode->nextListItem == nullptr);
firstChildElement.append (newNode);
}
}
void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept
{
if (newNode != nullptr)
{
// The element being added must not be a child of another node!
jassert (newNode->nextListItem == nullptr);
firstChildElement.insertAtIndex (indexToInsertAt, newNode);
}
}
void XmlElement::prependChildElement (XmlElement* newNode) noexcept
{
if (newNode != nullptr)
{
// The element being added must not be a child of another node!
jassert (newNode->nextListItem == nullptr);
firstChildElement.insertNext (newNode);
}
}
XmlElement* XmlElement::createNewChildElement (StringRef childTagName)
{
XmlElement* const newElement = new XmlElement (childTagName);
addChildElement (newElement);
return newElement;
}
bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
XmlElement* const newNode) noexcept
{
if (newNode != nullptr)
{
if (LinkedListPointer<XmlElement>* const p = firstChildElement.findPointerTo (currentChildElement))
{
if (currentChildElement != newNode)
delete p->replaceNext (newNode);
return true;
}
}
return false;
}
void XmlElement::removeChildElement (XmlElement* const childToRemove,
const bool shouldDeleteTheChild) noexcept
{
if (childToRemove != nullptr)
{
firstChildElement.remove (childToRemove);
if (shouldDeleteTheChild)
delete childToRemove;
}
}
bool XmlElement::isEquivalentTo (const XmlElement* const other,
const bool ignoreOrderOfAttributes) const noexcept
{
if (this != other)
{
if (other == nullptr || tagName != other->tagName)
return false;
if (ignoreOrderOfAttributes)
{
int totalAtts = 0;
for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
{
if (! other->compareAttribute (att->name, att->value))
return false;
++totalAtts;
}
if (totalAtts != other->getNumAttributes())
return false;
}
else
{
const XmlAttributeNode* thisAtt = attributes;
const XmlAttributeNode* otherAtt = other->attributes;
for (;;)
{
if (thisAtt == nullptr || otherAtt == nullptr)
{
if (thisAtt == otherAtt) // both nullptr, so it's a match
break;
return false;
}
if (thisAtt->name != otherAtt->name
|| thisAtt->value != otherAtt->value)
{
return false;
}
thisAtt = thisAtt->nextListItem;
otherAtt = otherAtt->nextListItem;
}
}
const XmlElement* thisChild = firstChildElement;
const XmlElement* otherChild = other->firstChildElement;
for (;;)
{
if (thisChild == nullptr || otherChild == nullptr)
{
if (thisChild == otherChild) // both 0, so it's a match
break;
return false;
}
if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
return false;
thisChild = thisChild->nextListItem;
otherChild = otherChild->nextListItem;
}
}
return true;
}
void XmlElement::deleteAllChildElements() noexcept
{
firstChildElement.deleteAll();
}
void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept
{
for (XmlElement* child = firstChildElement; child != nullptr;)
{
XmlElement* const nextChild = child->nextListItem;
if (child->hasTagName (name))
removeChildElement (child, true);
child = nextChild;
}
}
bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
{
return firstChildElement.contains (possibleChild);
}
XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
{
if (this == elementToLookFor || elementToLookFor == nullptr)
return nullptr;
for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
{
if (elementToLookFor == child)
return this;
if (XmlElement* const found = child->findParentElementOf (elementToLookFor))
return found;
}
return nullptr;
}
void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
{
firstChildElement.copyToArray (elems);
}
void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept
{
XmlElement* e = firstChildElement = elems[0];
for (int i = 1; i < num; ++i)
{
e->nextListItem = elems[i];
e = e->nextListItem;
}
e->nextListItem = nullptr;
}
//==============================================================================
bool XmlElement::isTextElement() const noexcept
{
return tagName.isEmpty();
}
static const String juce_xmltextContentAttributeName ("text");
const String& XmlElement::getText() const noexcept
{
jassert (isTextElement()); // you're trying to get the text from an element that
// isn't actually a text element.. If this contains text sub-nodes, you
// probably want to use getAllSubText instead.
return getStringAttribute (juce_xmltextContentAttributeName);
}
void XmlElement::setText (const String& newText)
{
if (isTextElement())
setAttribute (juce_xmltextContentAttributeName, newText);
else
jassertfalse; // you can only change the text in a text element, not a normal one.
}
String XmlElement::getAllSubText() const
{
if (isTextElement())
return getText();
if (getNumChildElements() == 1)
return firstChildElement.get()->getAllSubText();
MemoryOutputStream mem (1024);
for (const XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
mem << child->getAllSubText();
return mem.toUTF8();
}
String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const
{
if (const XmlElement* const child = getChildByName (childTagName))
return child->getAllSubText();
return defaultReturnValue;
}
XmlElement* XmlElement::createTextElement (const String& text)
{
XmlElement* const e = new XmlElement ((int) 0);
e->setAttribute (juce_xmltextContentAttributeName, text);
return e;
}
bool XmlElement::isValidXmlName (StringRef text) noexcept
{
if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance()))
return false;
for (;;)
{
if (text.isEmpty())
return true;
if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance()))
return false;
}
}
void XmlElement::addTextElement (const String& text)
{
addChildElement (createTextElement (text));
}
void XmlElement::deleteAllTextElements() noexcept
{
for (XmlElement* child = firstChildElement; child != nullptr;)
{
XmlElement* const next = child->nextListItem;
if (child->isTextElement())
removeChildElement (child, true);
child = next;
}
}

+ 775
- 0
source/modules/juce_audio_graph/xml/juce_XmlElement.h View File

@@ -0,0 +1,775 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software 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.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_XMLELEMENT_H_INCLUDED
#define JUCE_XMLELEMENT_H_INCLUDED
//==============================================================================
/** 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&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
XmlElement (XmlElement&&) noexcept;
XmlElement& operator= (XmlElement&&) noexcept;
#endif
/** 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 attiributes.
The elements are only considered equivalent if they contain the same attiributes
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)
{
const int num = getNumChildElements();
if (num > 1)
{
HeapBlock<XmlElement*> elems ((size_t) 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 secton 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)
};
#endif // JUCE_XMLELEMENT_H_INCLUDED

Loading…
Cancel
Save