Files
GDevelop/Core/GDCore/IDE/WholeProjectRefactorer.cpp
Florian Rival f2ec46ca1e Enable more warnings from Clang when compiling C++ sources (#3042)
Only show in developer changelog
2021-09-13 20:02:03 +02:00

1233 lines
50 KiB
C++

/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "WholeProjectRefactorer.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Events/EventsBehaviorRenamer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/ExpressionsParameterMover.h"
#include "GDCore/IDE/Events/ExpressionsRenamer.h"
#include "GDCore/IDE/Events/InstructionsParameterMover.h"
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/UnfilledRequiredBehaviorPropertyProblem.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/BehaviorContent.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace {
// These functions are doing the reverse of what is done when adding
// instructions/expression to extension/behaviors. If needed, they could be
// moved to gd::PlatformExtension to colocate the usage of the namespace
// separator?
gd::String GetEventsFunctionFullType(const gd::String& extensionName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + functionName;
}
gd::String GetBehaviorEventsFunctionFullType(const gd::String& extensionName,
const gd::String& behaviorName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + behaviorName + separator + functionName;
}
gd::String GetBehaviorFullType(const gd::String& extensionName,
const gd::String& behaviorName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + behaviorName;
}
} // namespace
namespace gd {
// By convention, the first parameter of an events based behavior method is
// always called "Object".
const gd::String WholeProjectRefactorer::behaviorObjectParameterName = "Object";
void WholeProjectRefactorer::ExposeProjectEvents(
gd::Project& project, gd::ArbitraryEventsWorker& worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
worker.Launch(project.GetLayout(s).GetEvents());
}
// Add external events events
for (std::size_t s = 0; s < project.GetExternalEventsCount(); s++) {
worker.Launch(project.GetExternalEvents(s).GetEvents());
}
// Add events based extensions
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
// Add (free) events functions
auto& eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
worker.Launch(eventsFunction->GetEvents());
}
// Add (behavior) events functions
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction :
behaviorEventsFunctions.GetInternalVector()) {
worker.Launch(eventsFunction->GetEvents());
}
}
}
}
void WholeProjectRefactorer::ExposeProjectEvents(
gd::Project& project, gd::ArbitraryEventsWorkerWithContext& worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources) and ExposeProjectEffects (this time for
// effects).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
auto& layout = project.GetLayout(s);
worker.Launch(layout.GetEvents(), project, layout);
}
// Add external events events
for (std::size_t s = 0; s < project.GetExternalEventsCount(); s++) {
const auto& externalEvents = project.GetExternalEvents(s);
const gd::String& associatedLayout = externalEvents.GetAssociatedLayout();
if (project.HasLayoutNamed(associatedLayout)) {
worker.Launch(project.GetExternalEvents(s).GetEvents(),
project,
project.GetLayout(associatedLayout));
}
}
// Add events based extensions
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
// Add (free) events functions
auto& eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, *eventsFunction, globalObjectsAndGroups, objectsAndGroups);
worker.Launch(eventsFunction->GetEvents(),
globalObjectsAndGroups,
objectsAndGroups);
}
// Add (behavior) events functions
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) {
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
}
}
}
void WholeProjectRefactorer::ExposeEventsBasedBehaviorEvents(
gd::Project& project,
const gd::EventsBasedBehavior& eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext& worker) {
auto& behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
project,
eventsBasedBehavior,
*eventsFunction,
globalObjectsAndGroups,
objectsAndGroups);
worker.Launch(
eventsFunction->GetEvents(), globalObjectsAndGroups, objectsAndGroups);
}
}
void WholeProjectRefactorer::ExposeProjectObjects(
gd::Project& project, gd::ArbitraryObjectsWorker& worker) {
worker.Launch(project);
for (size_t i = 0; i < project.GetLayoutsCount(); i++)
worker.Launch(project.GetLayout(i));
};
std::set<gd::String>
WholeProjectRefactorer::GetAllObjectTypesUsingEventsBasedBehavior(
const gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior) {
std::set<gd::String> allTypes;
const gd::String behaviorType = GetBehaviorFullType(
eventsFunctionsExtension.GetName(), eventsBasedBehavior.GetName());
auto addTypesOfObjectsIn =
[&allTypes, &behaviorType](const gd::ObjectsContainer& objectsContainer) {
for (auto& object : objectsContainer.GetObjects()) {
for (auto& behaviorContent : object->GetAllBehaviorContents()) {
if (behaviorContent.second->GetTypeName() == behaviorType) {
allTypes.insert(object->GetType());
}
}
}
};
addTypesOfObjectsIn(project);
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
auto& layout = project.GetLayout(s);
addTypesOfObjectsIn(layout);
}
return allTypes;
}
void WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior) {
for (auto& eventsFunction :
eventsBasedBehavior.GetEventsFunctions().GetInternalVector()) {
auto& parameters = eventsFunction->GetParameters();
while (parameters.size() < 2) {
gd::ParameterMetadata newParameter;
parameters.push_back(newParameter);
}
parameters[0]
.SetType("object")
.SetName(behaviorObjectParameterName)
.SetDescription("Object")
.SetExtraInfo(eventsBasedBehavior.GetObjectType());
parameters[1]
.SetType("behavior")
.SetName("Behavior")
.SetDescription("Behavior")
.SetExtraInfo(GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()));
}
}
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::String& oldName,
const gd::String& newName) {
auto renameEventsFunction =
[&project, &oldName, &newName](const gd::EventsFunction& eventsFunction) {
DoRenameEventsFunction(
project,
eventsFunction,
GetEventsFunctionFullType(oldName, eventsFunction.GetName()),
GetEventsFunctionFullType(newName, eventsFunction.GetName()));
};
auto renameBehaviorEventsFunction =
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(oldName,
eventsBasedBehavior.GetName(),
eventsFunction.GetName()),
GetBehaviorEventsFunctionFullType(newName,
eventsBasedBehavior.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the extension name
}
};
auto renameBehaviorPropertyFunctions =
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyActionName(
property.GetName())),
GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyActionName(
property.GetName())));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyConditionName(
property.GetName())),
GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyConditionName(
property.GetName())));
ExposeProjectEvents(project, conditionRenamer);
// Nothing to do for expressions, expressions are not including the
// extension name
};
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
// Free expressions
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
renameEventsFunction(*eventsFunction);
}
}
// Behavior expressions
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
renameBehaviorEventsFunction(*eventsBasedBehavior, *eventsFunction);
}
}
}
// Free instructions
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
renameEventsFunction(*eventsFunction);
}
}
// Behavior instructions
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
renameBehaviorEventsFunction(*eventsBasedBehavior, *eventsFunction);
}
}
}
// Behavior properties
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorProperties = eventsBasedBehavior->GetPropertyDescriptors();
for (auto&& propertyDescriptor : behaviorProperties.GetInternalVector()) {
renameBehaviorPropertyFunctions(*eventsBasedBehavior,
*propertyDescriptor);
}
}
// Finally, rename behaviors used in objects
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
DoRenameBehavior(
project,
GetBehaviorFullType(oldName, eventsBasedBehavior->GetName()),
GetBehaviorFullType(newName, eventsBasedBehavior->GetName()));
}
}
void WholeProjectRefactorer::RenameEventsFunction(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::String& oldFunctionName,
const gd::String& newFunctionName) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(oldFunctionName)) return;
const gd::EventsFunction& eventsFunction =
eventsFunctionsExtension.GetEventsFunction(oldFunctionName);
DoRenameEventsFunction(
project,
eventsFunction,
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
oldFunctionName),
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
newFunctionName));
}
void WholeProjectRefactorer::RenameBehaviorEventsFunction(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& oldFunctionName,
const gd::String& newFunctionName) {
auto& eventsFunctions = eventsBasedBehavior.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(oldFunctionName)) return;
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(oldFunctionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
oldFunctionName),
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
}
void WholeProjectRefactorer::MoveEventsFunctionParameter(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::String& functionName,
std::size_t oldIndex,
std::size_t newIndex) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(functionName)) return;
const gd::EventsFunction& eventsFunction =
eventsFunctionsExtension.GetEventsFunction(functionName);
const gd::String& eventsFunctionType = GetEventsFunctionFullType(
eventsFunctionsExtension.GetName(), functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetFreeExpressionMovedParameter(
eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& functionName,
std::size_t oldIndex,
std::size_t newIndex) {
auto& eventsFunctions = eventsBasedBehavior.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(functionName)) return;
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(functionName);
const gd::String& eventsFunctionType =
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetBehaviorExpressionMovedParameter(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
functionName,
oldIndex,
newIndex);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& oldPropertyName,
const gd::String& newPropertyName) {
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
if (!properties.Has(oldPropertyName)) return;
if (properties.Get(oldPropertyName).GetType() == "Behavior") {
// This is a property representing another behavior that must exist on the
// object.
// This other "required behavior" uses the property name, that is about to
// change, as its name.
// So we must change all reference to this name in the events of the
// behavior functions.
gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(),
behaviorObjectParameterName,
oldPropertyName,
newPropertyName);
ExposeEventsBasedBehaviorEvents(
project, eventsBasedBehavior, behaviorRenamer);
} else {
// Properties that represent primitive values will be used through
// their related actions/conditions/expressions. Rename these.
// Order is important: we first rename the expressions then the
// instructions, to avoid being unable to fetch the metadata (the types of
// parameters) of instructions after they are renamed.
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
ExposeProjectEvents(project, expressionRenamer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyActionName(oldPropertyName)),
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyActionName(newPropertyName)));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(oldPropertyName)),
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(newPropertyName)));
ExposeProjectEvents(project, conditionRenamer);
}
}
void WholeProjectRefactorer::AddBehaviorAndRequiredBehaviors(
gd::Project& project,
gd::Object& object,
const gd::String& behaviorType,
const gd::String& behaviorName) {
if (object.AddNewBehavior(project, behaviorType, behaviorName) == nullptr) {
// The behavior type/metadata can't be found.
return;
};
const gd::Platform& platform = project.GetCurrentPlatform();
const gd::BehaviorMetadata& behaviorMetadata =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
// Should not happen because the behavior was added successfully (so its
// metadata are valid) - but double check anyway and bail out if the
// behavior metadata are invalid.
return;
}
gd::Behavior& behavior = behaviorMetadata.Get();
gd::BehaviorContent& behaviorContent = object.GetBehavior(behaviorName);
for (auto const& keyValue :
behavior.GetProperties(behaviorContent.GetContent())) {
const gd::String& propertyName = keyValue.first;
const gd::PropertyDescriptor& property = keyValue.second;
if (property.GetType().LowerCase() == "behavior") {
const std::vector<gd::String>& extraInfo = property.GetExtraInfo();
if (extraInfo.size() == 0) {
// very unlikely
continue;
}
const gd::String& requiredBehaviorType = extraInfo.at(0);
const auto behaviorContents =
WholeProjectRefactorer::GetBehaviorsWithType(object,
requiredBehaviorType);
const gd::String* defaultBehaviorName = nullptr;
if (behaviorContents.size() == 0) {
const gd::BehaviorMetadata& requiredBehaviorMetadata =
MetadataProvider::GetBehaviorMetadata(platform,
requiredBehaviorType);
const gd::String& requiredBehaviorName =
requiredBehaviorMetadata.GetDefaultName();
WholeProjectRefactorer::AddBehaviorAndRequiredBehaviors(
project, object, requiredBehaviorType, requiredBehaviorName);
defaultBehaviorName = &requiredBehaviorName;
} else {
defaultBehaviorName = &behaviorContents.at(0);
}
behavior.UpdateProperty(
behaviorContent.GetContent(), propertyName, *defaultBehaviorName);
}
}
}
std::vector<gd::String> WholeProjectRefactorer::GetBehaviorsWithType(
const gd::Object& object, const gd::String& type) {
std::vector<gd::String> behaviors;
for (auto& behaviorName : object.GetAllBehaviorNames()) {
const gd::BehaviorContent& behaviorContent =
object.GetBehavior(behaviorName);
if (behaviorContent.GetTypeName() == type) {
behaviors.push_back(behaviorName);
}
}
return behaviors;
}
std::vector<gd::String> WholeProjectRefactorer::FindDependentBehaviorNames(
const gd::Project& project,
const gd::Object& object,
const gd::String& behaviorName) {
std::unordered_set<gd::String> dependentBehaviorNames;
WholeProjectRefactorer::FindDependentBehaviorNames(
project, object, behaviorName, dependentBehaviorNames);
std::vector<gd::String> results;
results.insert(results.end(),
dependentBehaviorNames.begin(),
dependentBehaviorNames.end());
return results;
}
void WholeProjectRefactorer::FindDependentBehaviorNames(
const gd::Project& project,
const gd::Object& object,
const gd::String& behaviorName,
std::unordered_set<gd::String>& dependentBehaviorNames) {
const gd::Platform& platform = project.GetCurrentPlatform();
for (auto const& objectBehaviorName : object.GetAllBehaviorNames()) {
const gd::BehaviorContent& behaviorContent =
object.GetBehavior(objectBehaviorName);
const auto& behaviorMetadata = MetadataProvider::GetBehaviorMetadata(
platform, behaviorContent.GetTypeName());
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
// Ignore this behavior as it's unknown.
continue;
}
gd::Behavior& behavior = behaviorMetadata.Get();
for (auto const& keyValue :
behavior.GetProperties(behaviorContent.GetContent())) {
const gd::String& propertyName = keyValue.first;
const gd::PropertyDescriptor& property = keyValue.second;
if (property.GetType().LowerCase() == "behavior" &&
property.GetValue() == behaviorName &&
dependentBehaviorNames.find(objectBehaviorName) ==
dependentBehaviorNames.end()) {
dependentBehaviorNames.insert(objectBehaviorName);
WholeProjectRefactorer::FindDependentBehaviorNames(
project, object, objectBehaviorName, dependentBehaviorNames);
}
}
}
};
std::vector<gd::UnfilledRequiredBehaviorPropertyProblem>
WholeProjectRefactorer::FindInvalidRequiredBehaviorProperties(
const gd::Project& project) {
std::vector<gd::UnfilledRequiredBehaviorPropertyProblem>
invalidRequiredBehaviorProperties;
auto findInvalidRequiredBehaviorPropertiesInObjects =
[&project, &invalidRequiredBehaviorProperties](
const std::vector<std::unique_ptr<gd::Object> >& objectsList) {
for (auto& object : objectsList) {
for (auto& behaviorContentKeyValuePair :
object->GetAllBehaviorContents()) {
gd::BehaviorContent& behaviorContent =
*behaviorContentKeyValuePair.second;
const auto& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(),
behaviorContent.GetTypeName());
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
std::cout << "Could not find metadata for behavior with type \""
<< behaviorContent.GetTypeName() << "\"" << std::endl;
continue;
}
const auto& behavior = behaviorMetadata.Get();
for (auto const& keyValue :
behavior.GetProperties(behaviorContent.GetContent())) {
const gd::String& propertyName = keyValue.first;
const gd::PropertyDescriptor& property = keyValue.second;
if (property.GetType().LowerCase() != "behavior") {
continue;
}
const gd::String& requiredBehaviorName = property.GetValue();
const std::vector<gd::String>& extraInfo =
property.GetExtraInfo();
if (extraInfo.size() == 0) {
// very unlikely
continue;
}
const gd::String& requiredBehaviorType = extraInfo.at(0);
if (requiredBehaviorName == "" ||
!object->HasBehaviorNamed(requiredBehaviorName) ||
object->GetBehavior(requiredBehaviorName).GetTypeName() !=
requiredBehaviorType) {
auto problem = UnfilledRequiredBehaviorPropertyProblem(
project,
*object,
behaviorContent,
propertyName,
requiredBehaviorType);
invalidRequiredBehaviorProperties.push_back(problem);
}
}
}
}
};
// Find in global objects
findInvalidRequiredBehaviorPropertiesInObjects(project.GetObjects());
// Find in layout objects.
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
const gd::Layout& layout = project.GetLayout(i);
findInvalidRequiredBehaviorPropertiesInObjects(layout.GetObjects());
}
return invalidRequiredBehaviorProperties;
}
bool WholeProjectRefactorer::FixInvalidRequiredBehaviorProperties(
gd::Project& project) {
const auto& invalidRequiredBehaviorProblems =
FindInvalidRequiredBehaviorProperties(project);
for (const auto& problem : invalidRequiredBehaviorProblems) {
auto& object = problem.GetSourceObject();
auto suggestedBehaviorNames =
GetBehaviorsWithType(object, problem.GetExpectedBehaviorTypeName());
auto& behaviorContent = problem.GetSourceBehaviorContent();
auto& behaviorMetadata = MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(), behaviorContent.GetTypeName());
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
continue;
}
auto& behavior = behaviorMetadata.Get();
if (suggestedBehaviorNames.empty()) {
// No matching behavior on the object.
// Add required behaviors on the object.
auto& expectedBehaviorMetadata = MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(), problem.GetExpectedBehaviorTypeName());
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
continue;
}
const gd::String& newBehaviorName =
expectedBehaviorMetadata.GetDefaultName();
AddBehaviorAndRequiredBehaviors(project,
object,
problem.GetExpectedBehaviorTypeName(),
newBehaviorName);
behavior.UpdateProperty(behaviorContent.GetContent(),
problem.GetSourcePropertyName(),
newBehaviorName);
} else {
// There is a matching behavior on the object use it by default.
behavior.UpdateProperty(
behaviorContent.GetContent(),
problem.GetSourcePropertyName(),
// It's unlikely the object has 2 behaviors of the same type.
suggestedBehaviorNames[0]);
}
}
return !invalidRequiredBehaviorProblems.empty();
}
void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::String& oldBehaviorName,
const gd::String& newBehaviorName) {
auto& eventsBasedBehaviors =
eventsFunctionsExtension.GetEventsBasedBehaviors();
if (!eventsBasedBehaviors.Has(oldBehaviorName)) {
gd::LogWarning("Warning, " + oldBehaviorName +
" was not found when calling RenameEventsBasedBehavior.");
return;
}
auto& eventsBasedBehavior = eventsBasedBehaviors.Get(oldBehaviorName);
auto renameBehaviorEventsFunction =
[&project,
&eventsFunctionsExtension,
&oldBehaviorName,
&newBehaviorName](const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
eventsFunction.GetName()),
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the name of the
// behavior
}
};
auto renameBehaviorProperty = [&project,
&eventsFunctionsExtension,
&oldBehaviorName,
&newBehaviorName](
const gd::NamedPropertyDescriptor&
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())),
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())),
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())));
ExposeProjectEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the behavior
};
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
auto& behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
// Behavior expressions
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
renameBehaviorEventsFunction(*eventsFunction);
}
}
// Behavior instructions
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
renameBehaviorEventsFunction(*eventsFunction);
}
}
// Behavior properties
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto&& property : properties.GetInternalVector()) {
renameBehaviorProperty(*property);
}
DoRenameBehavior(
project,
GetBehaviorFullType(eventsFunctionsExtension.GetName(), oldBehaviorName),
GetBehaviorFullType(eventsFunctionsExtension.GetName(), newBehaviorName));
}
void WholeProjectRefactorer::DoRenameEventsFunction(
gd::Project& project,
const gd::EventsFunction& eventsFunction,
const gd::String& oldFullType,
const gd::String& newFullType) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer =
gd::InstructionsTypeRenamer(project, oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedFreeExpression(oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
}
}
void WholeProjectRefactorer::DoRenameBehavior(
gd::Project& project,
const gd::String& oldBehaviorType,
const gd::String& newBehaviorType) {
auto renameBehaviorTypeInBehaviorContent =
[&oldBehaviorType,
&newBehaviorType](gd::BehaviorContent& behaviorContent) {
if (behaviorContent.GetTypeName() == oldBehaviorType) {
behaviorContent.SetTypeName(newBehaviorType);
}
};
auto renameBehaviorTypeInObjects =
[&renameBehaviorTypeInBehaviorContent](
std::vector<std::unique_ptr<gd::Object> >& objectsList) {
for (auto& object : objectsList) {
for (auto& behaviorContent : object->GetAllBehaviorContents()) {
renameBehaviorTypeInBehaviorContent(*behaviorContent.second);
}
}
};
auto renameBehaviorTypeInParameters =
[&oldBehaviorType, &newBehaviorType](gd::EventsFunction& eventsFunction) {
for (auto& parameter : eventsFunction.GetParameters()) {
if (gd::ParameterMetadata::IsBehavior(parameter.GetType()) &&
parameter.GetExtraInfo() == oldBehaviorType) {
parameter.SetExtraInfo(newBehaviorType);
}
}
};
// Rename behavior in required behavior properties
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto& eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) {
for (size_t i = 0;
i < eventsBasedBehavior->GetPropertyDescriptors().GetCount();
i++) {
NamedPropertyDescriptor& propertyDescriptor =
eventsBasedBehavior->GetPropertyDescriptors().Get(i);
std::vector<gd::String>& extraInfo = propertyDescriptor.GetExtraInfo();
if (propertyDescriptor.GetType() == "Behavior" &&
extraInfo.size() > 0) {
const gd::String& requiredBehaviorType = extraInfo[0];
if (requiredBehaviorType == oldBehaviorType) {
extraInfo[0] = newBehaviorType;
}
}
}
}
}
// Rename behavior in global objects
renameBehaviorTypeInObjects(project.GetObjects());
// Rename behavior in layout objects and layout behavior shared data.
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
gd::Layout& layout = project.GetLayout(i);
renameBehaviorTypeInObjects(layout.GetObjects());
for (auto& behaviorSharedDataContent : layout.GetAllBehaviorSharedData()) {
renameBehaviorTypeInBehaviorContent(*behaviorSharedDataContent.second);
}
}
// Rename in parameters of (free/behavior) events function
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto& eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
renameBehaviorTypeInParameters(*eventsFunction);
}
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction :
behaviorEventsFunctions.GetInternalVector()) {
renameBehaviorTypeInParameters(*eventsFunction);
}
}
}
}
void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
gd::Project& project,
gd::Layout& layout,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups) {
// Remove object in the current layout
if (removeEventsAndGroups) {
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
layout.GetEvents(),
objectName);
}
if (!isObjectGroup) { // Object groups can't have instances or be in other
// groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
if (layout.GetObjectGroups()[g].Find(objectName))
layout.GetObjectGroups()[g].RemoveObject(objectName);
}
}
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
}
// Remove object in external events
if (removeEventsAndGroups) {
DependenciesAnalyzer analyzer(project, layout);
if (analyzer.Analyze()) {
for (auto& externalEventsName :
analyzer.GetExternalEventsDependencies()) {
auto& externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
externalEvents.GetEvents(),
objectName);
}
for (auto& layoutName : analyzer.GetScenesDependencies()) {
auto& layout = project.GetLayout(layoutName);
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
layout.GetEvents(),
objectName);
}
}
}
// Remove object in external layouts
if (!isObjectGroup) { // Object groups can't have instances
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto& externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject(
objectName);
}
}
}
void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
gd::Project& project,
gd::Layout& layout,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
layout.GetEvents(),
oldName,
newName);
if (!isObjectGroup) { // Object groups can't have instances or be in other
// groups
layout.GetInitialInstances().RenameInstancesOfObject(oldName, newName);
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
layout.GetObjectGroups()[g].RenameObject(oldName, newName);
}
}
// Rename object in external events
DependenciesAnalyzer analyzer(project, layout);
if (analyzer.Analyze()) {
for (auto& externalEventsName : analyzer.GetExternalEventsDependencies()) {
auto& externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
externalEvents.GetEvents(),
oldName,
newName);
}
for (auto& layoutName : analyzer.GetScenesDependencies()) {
auto& layout = project.GetLayout(layoutName);
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
layout.GetEvents(),
oldName,
newName);
}
}
// Rename object in external layouts
if (!isObjectGroup) { // Object groups can't have instances
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto& externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().RenameInstancesOfObject(oldName,
newName);
}
}
}
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
gd::Project& project,
gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups) {
// Remove object in the current layout
if (removeEventsAndGroups) {
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
globalObjectsContainer,
objectsContainer,
eventsFunction.GetEvents(),
objectName);
}
if (!isObjectGroup) { // Object groups can't be in other groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size();
++g) {
if (eventsFunction.GetObjectGroups()[g].Find(objectName))
eventsFunction.GetObjectGroups()[g].RemoveObject(objectName);
}
}
}
}
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
gd::Project& project,
gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
globalObjectsContainer,
objectsContainer,
eventsFunction.GetEvents(),
oldName,
newName);
if (!isObjectGroup) { // Object groups can't be in other groups
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size(); ++g) {
eventsFunction.GetObjectGroups()[g].RenameObject(oldName, newName);
}
}
}
void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
gd::Project& project,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
if (!isObjectGroup) { // Object groups can't be in other groups
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RenameObject(oldName, newName);
}
}
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
gd::Layout& layout = project.GetLayout(i);
if (layout.HasObjectNamed(oldName)) continue;
ObjectOrGroupRenamedInLayout(
project, layout, oldName, newName, isObjectGroup);
}
}
void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
gd::Project& project,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups) {
if (!isObjectGroup) { // Object groups can't be in other groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RemoveObject(objectName);
}
}
}
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
gd::Layout& layout = project.GetLayout(i);
if (layout.HasObjectNamed(objectName)) continue;
ObjectOrGroupRemovedInLayout(
project, layout, objectName, isObjectGroup, removeEventsAndGroups);
}
}
std::vector<gd::String> WholeProjectRefactorer::GetAssociatedExternalLayouts(
gd::Project& project, gd::Layout& layout) {
std::vector<gd::String> results;
for (std::size_t i = 0; i < project.GetExternalLayoutsCount(); ++i) {
auto& externalLayout = project.GetExternalLayout(i);
if (externalLayout.GetAssociatedLayout() == layout.GetName()) {
results.push_back(externalLayout.GetName());
}
}
return results;
}
} // namespace gd