1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/Projucer/Source/LiveBuildEngine/jucer_ClassDatabase.h
2017-12-19 12:44:24 +00:00

723 lines
23 KiB
C

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
struct ClassDatabase
{
//==============================================================================
struct MemberInfo
{
enum CodeLocationType
{
declaration = 0,
addedToParent,
setBoundsParamX,
setBoundsParamY,
setBoundsParamW,
setBoundsParamH,
// WARNING! When you change any of these, also update the copy that lives in the live editing code
numCodeLocationTypes
};
MemberInfo() {}
MemberInfo (const MemberInfo& other)
: name (other.name), type (other.type)
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i] = other.locations[i];
}
MemberInfo (const String& nm, const String& ty)
: name (nm), type (ty)
{
}
MemberInfo (const ValueTree& v)
: name (v [Ids::name].toString()),
type (v [Ids::class_].toString())
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i] = v [getIdentifierForCodeLocationType (i)].toString();
}
const String& getName() const { return name; }
const String& getType() const { return type; }
const SourceCodeRange& getLocation (CodeLocationType t) const
{
return locations[t];
}
void setLocation (CodeLocationType t, const SourceCodeRange& range)
{
locations[t] = range;
}
void mergeWith (const MemberInfo& other)
{
jassert (name == other.name);
if (other.type.isNotEmpty())
type = other.type;
for (int i = 0; i < numCodeLocationTypes; ++i)
if (other.locations[i].isValid())
locations[i] = other.locations[i];
}
void nudgeAllCodeRanges (const String& file, const int insertPoint, const int delta)
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i].nudge (file, insertPoint, delta);
}
void fileContentChanged (const String& file)
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i].fileContentChanged (file);
}
ValueTree toValueTree() const
{
ValueTree m (Ids::MEMBER);
m.setProperty (Ids::name, name, nullptr);
m.setProperty (Ids::class_, type, nullptr);
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i].writeToValueTree (m, getIdentifierForCodeLocationType (i));
return m;
}
private:
String name, type;
SourceCodeRange locations [numCodeLocationTypes];
static Identifier getIdentifierForCodeLocationType (int typeIndex)
{
// (These need to remain in order)
static_assert (setBoundsParamX + 1 == setBoundsParamY && setBoundsParamY + 1 == setBoundsParamW
&& setBoundsParamW + 1 == setBoundsParamH, "");
static const Identifier ids[] =
{
"declaration",
"addedToParent",
"setBoundsParamX",
"setBoundsParamY",
"setBoundsParamW",
"setBoundsParamH"
};
return ids [typeIndex];
}
};
//==============================================================================
struct MethodInfo
{
MethodInfo() {}
MethodInfo (const MethodInfo& other)
: name (other.name), returnType (other.returnType),
declaration (other.declaration), definition (other.definition),
numArgs (other.numArgs), flags (other.flags)
{
}
String name, returnType;
SourceCodeRange declaration, definition;
int numArgs, flags;
enum
{
isConstructor = 1,
isDefaultConstructor = 2,
isTemplated = 4,
isPublic = 8
};
MethodInfo (const ValueTree& v)
: name (v[Ids::name].toString()),
returnType (v[Ids::returnType].toString()),
declaration (v[Ids::declaration].toString()),
definition (v[Ids::definition].toString()),
numArgs (v[Ids::numArgs]),
flags (v[Ids::flags])
{
}
ValueTree toValueTree() const
{
ValueTree m (Ids::METHOD);
m.setProperty (Ids::name, name, nullptr);
m.setProperty (Ids::returnType, returnType, nullptr);
m.setProperty (Ids::numArgs, numArgs, nullptr);
m.setProperty (Ids::flags, flags, nullptr);
declaration.writeToValueTree (m, Ids::declaration);
definition.writeToValueTree (m, Ids::definition);
return m;
}
void nudgeAllCodeRanges (const String& file, const int insertPoint, const int delta)
{
declaration.nudge (file, insertPoint, delta);
definition.nudge (file, insertPoint, delta);
}
void fileContentChanged (const String& file)
{
declaration.fileContentChanged (file);
definition.fileContentChanged (file);
}
};
//==============================================================================
struct InstantiationFlags
{
InstantiationFlags()
: isAbstract (false),
inAnonymousNamespace (false),
noDefaultConstructor (false)
{}
InstantiationFlags (const InstantiationFlags& other)
: isAbstract (other.isAbstract),
inAnonymousNamespace (other.inAnonymousNamespace),
noDefaultConstructor (other.noDefaultConstructor)
{}
bool canBeInstantiated() const noexcept
{
return ! (isAbstract || inAnonymousNamespace || noDefaultConstructor);
}
String getReasonForUnavailability() const
{
if (isAbstract) return "This class is abstract";
if (noDefaultConstructor) return "This class has no default constructor";
if (inAnonymousNamespace) return "This class is declared inside an anonymous namespace";
return String();
}
bool isDisallowed (const InstantiationFlags& disallowedFlags) const
{
return ! ((disallowedFlags.isAbstract && isAbstract)
|| (disallowedFlags.inAnonymousNamespace && inAnonymousNamespace)
|| (disallowedFlags.noDefaultConstructor && noDefaultConstructor));
}
bool isAbstract;
bool inAnonymousNamespace;
bool noDefaultConstructor;
};
//==============================================================================
struct Class
{
Class() {}
~Class() {}
Class (const Class& other)
: className (other.className), members (other.members),
methods (other.methods), classDeclaration (other.classDeclaration),
instantiationFlags (other.instantiationFlags)
{
}
Class (const String& name, const InstantiationFlags& flags,
const Array<MemberInfo>& m,
const Array<MethodInfo>& meth,
const SourceCodeRange& classDeclarationRange)
: className (name),
members (m), methods (meth),
classDeclaration (classDeclarationRange),
instantiationFlags (flags)
{
}
Class& operator= (const Class& other)
{
className = other.className;
members = other.members;
methods = other.methods;
classDeclaration = other.classDeclaration;
instantiationFlags = other.instantiationFlags;
return *this;
}
const String& getName() const noexcept { return className; }
const InstantiationFlags& getInstantiationFlags() const
{
return instantiationFlags;
}
void setInstantiationFlags (const InstantiationFlags& newFlags)
{
instantiationFlags = newFlags;
}
const SourceCodeRange& getClassDeclarationRange() const
{
return classDeclaration;
}
const MemberInfo* findMember (const String& memberName) const
{
for (auto& m : members)
if (m.getName() == memberName)
return &m;
return nullptr;
}
MemberInfo* findMember (const String& memberName)
{
return const_cast<MemberInfo*> (static_cast<const Class&>(*this).findMember (memberName));
}
const MethodInfo* getDefaultConstructor() const
{
for (const MethodInfo& m : methods)
if ((m.flags & MethodInfo::isDefaultConstructor) != 0)
return &m;
return nullptr;
}
const MethodInfo* getConstructor() const
{
if (const MethodInfo* m = getDefaultConstructor())
return m;
for (const MethodInfo& m : methods)
if ((m.flags & MethodInfo::isConstructor) != 0)
return &m;
return nullptr;
}
const MethodInfo* getResizedMethod() const
{
for (const MethodInfo& m : methods)
if (m.name == "resized" && m.numArgs == 0)
return &m;
return nullptr;
}
File getMainSourceFile() const
{
if (const MethodInfo* m = getResizedMethod())
if (m->definition.isValid())
return m->definition.file;
if (const MethodInfo* m = getConstructor())
if (m->definition.isValid())
return m->definition.file;
for (auto& m : methods)
if (m.definition.isValid() && File (m.definition.file).hasFileExtension ("cpp;mm"))
return m.definition.file;
for (auto& m : methods)
if ((m.flags & MethodInfo::isConstructor) != 0 && m.definition.isValid())
return m.definition.file;
for (auto& m : methods)
if (m.definition.isValid() && File (m.definition.file).exists())
return m.definition.file;
return {};
}
Array<File> getAllSourceFiles() const
{
Array<File> files;
for (const MethodInfo& m : methods)
{
files.addIfNotAlreadyThere (m.declaration.file);
files.addIfNotAlreadyThere (m.definition.file);
}
return files;
}
bool isDeclaredInFile (const File& file) const
{
return file == classDeclaration.file;
}
void mergeWith (const Class& other)
{
jassert (*this == other);
if (other.classDeclaration.isValid())
classDeclaration = other.classDeclaration;
for (auto& m : other.members)
{
if (auto* existing = findMember (m.getName()))
existing->mergeWith (m);
else
members.add (m);
}
}
void nudgeAllCodeRanges (const String& file, int index, int delta)
{
for (MemberInfo& m : members) m.nudgeAllCodeRanges (file, index, delta);
for (MethodInfo& m : methods) m.nudgeAllCodeRanges (file, index, delta);
classDeclaration.nudge (file, index, delta);
}
void fileContentChanged (const String& file)
{
for (MemberInfo& m : members) m.fileContentChanged (file);
for (MethodInfo& m : methods) m.fileContentChanged (file);
classDeclaration.fileContentChanged (file);
}
Class (const ValueTree& v)
{
className = v[Ids::name];
instantiationFlags.isAbstract = v[Ids::abstract];
instantiationFlags.inAnonymousNamespace = v[Ids::anonymous];
instantiationFlags.noDefaultConstructor = v[Ids::noDefConstructor];
classDeclaration = v [Ids::classDecl].toString();
for (int i = 0; i < v.getNumChildren(); ++i)
members.add (MemberInfo (v.getChild(i)));
}
ValueTree toValueTree() const
{
ValueTree v (Ids::CLASS);
v.setProperty (Ids::name, className, nullptr);
v.setProperty (Ids::abstract, instantiationFlags.isAbstract, nullptr);
v.setProperty (Ids::anonymous, instantiationFlags.inAnonymousNamespace, nullptr);
v.setProperty (Ids::noDefConstructor, instantiationFlags.noDefaultConstructor, nullptr);
classDeclaration.writeToValueTree (v, Ids::classDecl);
for (const MemberInfo& m : members)
v.appendChild (m.toValueTree(), nullptr);
return v;
}
bool operator== (const Class& other) const noexcept { return className == other.className; }
bool operator!= (const Class& other) const noexcept { return ! operator== (other); }
bool operator< (const Class& other) const noexcept { return className < other.className; }
const Array<MemberInfo>& getMembers() const { return members; }
private:
String className;
Array<MemberInfo> members;
Array<MethodInfo> methods;
SourceCodeRange classDeclaration;
InstantiationFlags instantiationFlags;
JUCE_LEAK_DETECTOR (Class)
};
//==============================================================================
struct Namespace
{
Namespace() : name ("Global Namespace") {}
Namespace (const String& n, const String& full) : name (n), fullName (full) {}
bool isEmpty() const noexcept
{
for (const auto& n : namespaces)
if (! n.isEmpty())
return false;
return components.size() == 0;
}
int getTotalClassesAndNamespaces() const
{
int total = components.size();
for (const auto& n : namespaces)
total += n.getTotalClassesAndNamespaces();
return total;
}
void add (const Class& c, const String::CharPointerType& localName)
{
auto nextDoubleColon = CharacterFunctions::find (localName, CharPointer_ASCII ("::"));
if (nextDoubleColon.isEmpty())
merge (c);
else
getOrCreateNamespace (String (localName, nextDoubleColon))->add (c, nextDoubleColon + 2);
}
bool containsRecursively (const Class& c) const
{
if (components.contains (c))
return true;
for (const auto& n : namespaces)
if (n.containsRecursively (c))
return true;
return false;
}
const Class* findClass (const String& className) const
{
for (auto& c : components)
if (c.getName() == className)
return &c;
for (auto& n : namespaces)
if (auto* c = n.findClass (className))
return c;
return nullptr;
}
const MemberInfo* findClassMemberInfo (const String& className, const String& memberName) const
{
if (auto* classInfo = findClass (className))
return classInfo->findMember (memberName);
return nullptr;
}
void findClassesDeclaredInFile (Array<Class*>& results, const File& file)
{
for (auto& c : components)
if (c.isDeclaredInFile (file))
results.add (&c);
for (auto& n : namespaces)
n.findClassesDeclaredInFile (results, file);
}
void merge (const Namespace& other)
{
if (components.size() == 0)
{
components = other.components;
}
else
{
for (const auto& c : other.components)
merge (c);
}
for (const auto& n : other.namespaces)
getOrCreateNamespace (n.name)->merge (n);
}
void merge (const Class& c)
{
const int existing = components.indexOf (c);
if (existing < 0)
components.add (c);
else
components.getReference (existing).mergeWith (c);
}
Namespace* findNamespace (const String& targetName)
{
for (auto& n : namespaces)
if (n.name == targetName)
return &n;
return nullptr;
}
Namespace* createNamespace (const String& newName)
{
namespaces.add (Namespace (newName, fullName + "::" + newName));
return findNamespace (newName);
}
Namespace* getOrCreateNamespace (const String& newName)
{
if (Namespace* existing = findNamespace (newName))
return existing;
return createNamespace (newName);
}
void addInstantiableClasses (SortedSet<Class>& classes) const
{
for (const auto& c : components)
if (c.getInstantiationFlags().canBeInstantiated())
classes.add (c);
for (const auto& n : namespaces)
n.addInstantiableClasses (classes);
}
void swapWith (Namespace& other) noexcept
{
name.swapWith (other.name);
components.swapWith (other.components);
namespaces.swapWith (other.namespaces);
}
void nudgeAllCodeRanges (const String& file, int index, int delta)
{
for (auto& c : components) c.nudgeAllCodeRanges (file, index, delta);
for (auto& n : namespaces) n.nudgeAllCodeRanges (file, index, delta);
}
void fileContentChanged (const String& file)
{
for (auto& c : components) c.fileContentChanged (file);
for (auto& n : namespaces) n.fileContentChanged (file);
}
bool matches (const Namespace& other) const
{
if (name == other.name
&& components == other.components
&& namespaces.size() == other.namespaces.size())
{
for (int i = namespaces.size(); --i >= 0;)
if (! namespaces.getReference (i).matches (other.namespaces.getReference(i)))
return false;
return true;
}
return false;
}
void getAllClassNames (StringArray& results, const InstantiationFlags& disallowedFlags) const
{
for (const auto& c : components)
if (c.getInstantiationFlags().isDisallowed (disallowedFlags))
results.add (c.getName());
for (const auto& n : namespaces)
n.getAllClassNames (results, disallowedFlags);
}
ValueTree toValueTree() const
{
ValueTree v (Ids::CLASSLIST);
v.setProperty (Ids::name, name, nullptr);
for (const auto& c : components) v.appendChild (c.toValueTree(), nullptr);
for (const auto& n : namespaces) v.appendChild (n.toValueTree(), nullptr);
return v;
}
void loadFromValueTree (const ValueTree& v)
{
name = v[Ids::name];
for (int i = 0; i < v.getNumChildren(); ++i)
{
const ValueTree c (v.getChild(i));
if (c.hasType (Ids::CLASS))
components.add (Class (c));
else if (c.hasType (Ids::CLASSLIST))
createNamespace (c[Ids::name])->loadFromValueTree (c);
}
}
bool operator== (const Namespace& other) const noexcept { return name == other.name; }
bool operator!= (const Namespace& other) const noexcept { return ! operator== (other); }
bool operator< (const Namespace& other) const noexcept { return name < other.name; }
String name, fullName;
SortedSet<Class> components;
SortedSet<Namespace> namespaces;
JUCE_LEAK_DETECTOR (Namespace)
};
struct ClassList
{
ClassList() {}
void clear()
{
Namespace newNamespace;
globalNamespace.swapWith (newNamespace);
}
void registerComp (const Class& comp)
{
globalNamespace.add (comp, comp.getName().getCharPointer());
}
void merge (const ClassList& other)
{
globalNamespace.merge (other.globalNamespace);
}
void swapWith (ClassList& other) noexcept
{
globalNamespace.swapWith (other.globalNamespace);
}
//==============================================================================
ValueTree toValueTree() const
{
return globalNamespace.toValueTree();
}
static ClassList fromValueTree (const ValueTree& v)
{
ClassList l;
l.globalNamespace.loadFromValueTree (v);
return l;
}
Namespace globalNamespace;
bool operator== (const ClassList& other) const noexcept { return globalNamespace.matches (other.globalNamespace); }
bool operator!= (const ClassList& other) const noexcept { return ! operator== (other); }
private:
JUCE_LEAK_DETECTOR (ClassList)
};
};