1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "gradleprojectgenerator.h"
6#include "gradleasynparse.h"
7#include "properties/gradleconfigpropertywidget.h"
8
9#include "common/dialog/propertiesdialog.h"
10#include "services/window/windowservice.h"
11#include "services/builder/builderservice.h"
12#include "services/option/optionmanager.h"
13#include "properties/gradleconfigutil.h"
14#include "common/util/custompaths.h"
15#include "common/util/environment.h"
16#include "common/supportfile/dapconfig.h"
17#include "services/project/projectservice.h"
18
19#include <QtConcurrent>
20#include <QtXml>
21#include <QFileIconProvider>
22
23using namespace gradleConfig;
24using namespace dpfservice;
25enum_def(GradleShellKey, QString)
26{
27 enum_exp ScriptName = "gradlew";
28 enum_exp ScriptArg_Task = "tasks";
29};
30
31enum_def(GradleMenuKey, QString)
32{
33 enum_exp Build_Tasks = "Build tasks";
34 enum_exp Build_Setup_Tasks = "Build Setup tasks";
35 enum_exp Focumentation_Tasks = "Documentation tasks";
36 enum_exp Help_Tasks = "Help tasks";
37 enum_exp Verification_tasks = "Verification tasks";
38};
39
40struct GradleTasksHelp
41{
42 QString argsName;
43 QString argsHelp;
44};
45
46typedef QList<GradleTasksHelp> GradleTasksHelps;
47typedef QHash<QString, GradleTasksHelps> GradleTasks;
48
49class GradleProjectGeneratorPrivate
50{
51 friend class GradleProjectGenerator;
52 QStandardItem* configureRootItem {nullptr};
53 QMenu *gradleMenu {nullptr};
54 QProcess *menuGenProcess {nullptr};
55 QHash<QStandardItem*, GradleAsynParse*> projectParses {};
56private:
57
58 GradleTasks parseTasks(const QByteArray &data)
59 {
60 GradleTasks result;
61 QTextStream textStream(data);
62 while (!textStream.atEnd()) {
63 qInfo() << textStream.readLine();
64 }
65 return result;
66 }
67
68 GradleTasks getTasks(QString sourceFolder,
69 QString shellName = GradleShellKey::get()->ScriptName)
70 {
71 GradleTasks result;
72 QString program = sourceFolder + QDir::separator() +shellName;
73 QStringList args = {GradleShellKey::get()->ScriptArg_Task};
74 ProcessUtil::execute(program, args, sourceFolder, [&](const QByteArray &data){
75 result = parseTasks(data);
76 });
77 return result;
78 }
79};
80
81GradleProjectGenerator::GradleProjectGenerator()
82 : d(new GradleProjectGeneratorPrivate())
83{
84 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
85 using namespace dpfservice;
86 auto &ctx = dpfInstance.serviceContext();
87 ProjectService *projectService = ctx.service<ProjectService>(ProjectService::name());
88 if (!projectService) {
89 qCritical() << "Failed, not found service : projectService";
90 abort();
91 }
92}
93
94GradleProjectGenerator::~GradleProjectGenerator()
95{
96 qInfo() << __FUNCTION__;
97
98 if (d)
99 delete d;
100}
101
102QStringList GradleProjectGenerator::supportLanguages()
103{
104 return {dpfservice::MWMFA_JAVA};
105}
106
107QStringList GradleProjectGenerator::supportFileNames()
108{
109 return {"build.gradle", "settings.gradle"};
110}
111
112QDialog *GradleProjectGenerator::configureWidget(const QString &language,
113 const QString &projectPath)
114{
115 // get config result.
116 ProjectInfo projectInfo;
117 projectInfo.setLanguage(language);
118 projectInfo.setKitName(GradleProjectGenerator::toolKitName());
119 projectInfo.setWorkspaceFolder(projectPath);
120
121 // refresh config.
122 restoreRuntimeCfg(projectInfo);
123 configure(projectInfo);
124
125 return nullptr;
126}
127
128bool GradleProjectGenerator::configure(const dpfservice::ProjectInfo &info)
129{
130 dpfservice::ProjectGenerator::configure(info);
131
132 auto root = createRootItem(info);
133 using namespace dpfservice;
134 auto &ctx = dpfInstance.serviceContext();
135 ProjectService *projectService = ctx.service<ProjectService>(ProjectService::name());
136 if (projectService && root) {
137 projectService->projectView.addRootItem(root);
138 projectService->projectView.expandedDepth(root, 1);
139 }
140
141 dpfservice::ProjectGenerator::configure(info);
142
143 return true;
144}
145
146QStandardItem *GradleProjectGenerator::createRootItem(const dpfservice::ProjectInfo &info)
147{
148 using namespace dpfservice;
149 QStandardItem * rootItem = ProjectGenerator::createRootItem(info);
150 dpfservice::ProjectInfo::set(rootItem, info);
151 d->projectParses[rootItem] = new GradleAsynParse();
152 QObject::connect(d->projectParses[rootItem], &GradleAsynParse::itemsModified,
153 this, &GradleProjectGenerator::doProjectChildsModified);
154 QMetaObject::invokeMethod(d->projectParses[rootItem], "parseProject",
155 Q_ARG(const dpfservice::ProjectInfo &, info));
156 return rootItem;
157}
158
159void GradleProjectGenerator::removeRootItem(QStandardItem *root)
160{
161 if (!root)
162 return;
163 auto parser = d->projectParses[root];
164
165 while (root->hasChildren()) {
166 root->takeRow(0);
167 }
168 d->projectParses.remove(root);
169
170 delete root;
171
172 if (parser)
173 delete parser;
174}
175
176QMenu *GradleProjectGenerator::createItemMenu(const QStandardItem *item)
177{
178 if (item->parent())
179 return nullptr;
180
181 using namespace dpfservice;
182 ProjectInfo info = ProjectInfo::get(item);
183 if (!QFileInfo(info.workspaceFolder()).exists())
184 return nullptr;
185
186 QString program = info.workspaceFolder() + QDir::separator()
187 + GradleShellKey::get()->ScriptName;
188 QStringList args = {GradleShellKey::get()->ScriptArg_Task};
189 QMenu *menu = new QMenu();
190 if (!d->gradleMenu) {
191 d->gradleMenu = new QMenu("Gradle");
192 doGradleGeneratMenu(program, args, info.workspaceFolder()); // asyn
193 }
194 menu->addMenu(d->gradleMenu);
195
196 QStandardItem *itemTemp = const_cast<QStandardItem *>(item);
197 if (!itemTemp)
198 return d->gradleMenu;
199
200 QAction *action = new QAction(tr("Properties"));
201 menu->addAction(action);
202 QObject::connect(action, &QAction::triggered, [=](){
203 actionProperties(info, itemTemp);
204 });
205
206 return menu;
207}
208
209void GradleProjectGenerator::doProjectChildsModified(const QList<QStandardItem *> &items)
210{
211 auto rootItem = d->projectParses.key(qobject_cast<GradleAsynParse*>(sender()));
212 if (rootItem) {
213 while (rootItem->hasChildren()) {
214 rootItem->takeRow(0);
215 }
216 rootItem->appendRows(items);
217 }
218}
219
220void GradleProjectGenerator::doGradleGeneratMenu(const QString &program,
221 const QStringList &args,
222 const QString &workdir)
223{
224 struct {
225 QString operator ()(int count)
226 {
227 QString result;
228 for(int i= 0; i < count; i++) {
229 result += "-";
230 }
231 return result;
232 }
233 }taskChildSplit;
234
235 if (!d->menuGenProcess) {
236 d->menuGenProcess = new QProcess();
237
238 QObject::connect(d->menuGenProcess, &QProcess::errorOccurred,
239 [=](QProcess::ProcessError error){
240 qCritical() << "program: " << d->menuGenProcess->program()
241 << "aruments: " << d->menuGenProcess->arguments()
242 << "workdir: " << d->menuGenProcess->workingDirectory()
243 << "error" << error
244 << "errorString" << d->menuGenProcess->errorString();
245 });
246
247 QObject::connect(d->menuGenProcess,
248 QOverload<int, QProcess::ExitStatus>
249 ::of(&QProcess::finished),
250 [&](int, QProcess::ExitStatus)
251 {
252 if (d->menuGenProcess->canReadLine() && d->gradleMenu) {
253 QTextStream stream(d->menuGenProcess->readAll());
254 while (!stream.atEnd()) {
255 QString taskBegin = stream.readLine();
256 QMenu *menu = nullptr;
257 if (GradleMenuKey::contains(taskBegin)) {
258 menu = new QMenu(taskBegin);
259 menu->setToolTipsVisible(true);
260 }
261
262 if (menu) {
263 QString taskEnd = stream.readLine();
264 while (taskEnd != "") {
265 taskEnd = stream.readLine();
266 if (taskChildSplit(taskBegin.size())
267 == taskEnd) {
268 continue;
269 }
270 QStringList taskChild = taskEnd.split(" - ");
271 if (taskChild.size() == 2) {
272 QAction *action = new QAction(taskChild[0]);
273 qInfo() << taskChild[0] << taskChild[1];
274 action->setToolTip(taskChild[1]);
275 action->setProperty("kitName", GradleProjectGenerator::toolKitName());
276#ifdef __WIN32__
277 action->setPriority("program", "./gradlew.bat");
278#elif __linux__ || __apple__
279 action->setProperty("program", OptionManager::getInstance()->getGradleToolPath());
280#endif
281 action->setProperty("arguments", QStringList({"task", taskChild[0]}));
282 action->setProperty("workDir", d->menuGenProcess->workingDirectory());
283 QObject::connect(action, &QAction::triggered, this,
284 &GradleProjectGenerator::doGradleTaskActionTriggered,
285 Qt::UniqueConnection);
286 menu->addAction(action);
287 }
288 }
289 d->gradleMenu->addMenu(menu);
290 } // menu is create
291 } // while readline
292 } // if can read line
293 });
294 d->menuGenProcess->setProgram(program);
295 d->menuGenProcess->setArguments(args);
296 d->menuGenProcess->setWorkingDirectory(workdir);
297 d->menuGenProcess->start();
298 }
299}
300
301void GradleProjectGenerator::doGradleCleanMenu()
302{
303 if (d->gradleMenu) {
304 for (auto &action : d->gradleMenu->actions()) {
305 d->gradleMenu->removeAction(action);
306 }
307 }
308}
309
310void GradleProjectGenerator::doGradleTaskActionTriggered()
311{
312 QAction *action = qobject_cast<QAction*>(sender());
313 if (action) {
314 auto &ctx = dpfInstance.serviceContext();
315 auto builderService = ctx.service<dpfservice::BuilderService>(dpfservice::BuilderService::name());
316 if (builderService) {
317 BuildCommandInfo commandInfo;
318 commandInfo.kitName = action->property("kitName").toString();
319 commandInfo.program = action->property("program").toString();
320 commandInfo.arguments = action->property("arguments").toStringList();
321 commandInfo.workingDir = action->property("workDir").toString();
322 builderService->interface.builderCommand({commandInfo}, false);
323 }
324 }
325}
326
327void GradleProjectGenerator::actionProperties(const dpfservice::ProjectInfo &info, QStandardItem *item)
328{
329 PropertiesDialog dlg;
330 GradleConfigPropertyWidget *property = new GradleConfigPropertyWidget(info, item);
331 dlg.insertPropertyPanel("Config", property);
332 dlg.exec();
333}
334
335void GradleProjectGenerator::restoreRuntimeCfg(dpfservice::ProjectInfo &projectInfo)
336{
337 // get project config.
338 ConfigureParam *cfgParams = ConfigUtil::instance()->getConfigureParamPointer();
339 ConfigUtil::instance()->readConfig(ConfigUtil::instance()->getConfigPath(projectInfo.workspaceFolder()), *cfgParams);
340
341 // get global config.
342 QString arch = ProcessUtil::localPlatform();
343 QString dapSupportFilePath = support_file::DapSupportConfig::globalPath();
344 QString configHomePath = env::pkg::native::path();//CustomPaths::user(CustomPaths::Configures) + QDir::separator();
345 support_file::JavaDapPluginConfig javaDapPluginConfig;
346 bool ret = support_file::DapSupportConfig::readFromSupportFile(dapSupportFilePath, arch, javaDapPluginConfig, configHomePath);
347 if (!ret) {
348 qDebug("Read dapconfig.support failed, please check the file and retry.");
349 return;
350 }
351
352 // use global config when project config is null.
353 auto setPropertyParams = [&](QString &projectProperty, const QString &key, const QString &globalPropery){
354 if (projectProperty.isEmpty()) {
355 // use global propery.
356 projectInfo.setProperty(key, globalPropery);
357 projectProperty = globalPropery;
358 } else {
359 // use project propery.
360 projectInfo.setProperty(key, projectProperty);
361 }
362 };
363 setPropertyParams(cfgParams->jrePath, kJrePath, javaDapPluginConfig.jrePath);
364 setPropertyParams(cfgParams->jreExecute, kJreExecute, javaDapPluginConfig.jreExecute);
365 setPropertyParams(cfgParams->launchConfigPath, kLaunchConfigPath, javaDapPluginConfig.launchConfigPath);
366 setPropertyParams(cfgParams->launchPackageFile, kLaunchPackageFile, javaDapPluginConfig.launchPackageFile);
367 setPropertyParams(cfgParams->dapPackageFile, kDapPackageFile, javaDapPluginConfig.dapPackageFile);
368 projectInfo.setDetailInformation(cfgParams->detailInfo);
369}
370