1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.update.check.action.view; 17 18 import com.alibaba.fastjson.JSON; 19 import com.update.check.dto.UpdateCheckReportDto; 20 import com.update.check.utils.IoUtils; 21 import com.update.check.action.DataUpdateNotifier; 22 import com.update.check.dto.ApiDiffResultDto; 23 import com.update.check.dto.CollectApplicationApiDto; 24 import com.update.check.log.Logger; 25 import com.update.check.utils.FileUtils; 26 import com.intellij.openapi.fileChooser.FileChooserDescriptor; 27 import com.intellij.openapi.progress.ProgressManager; 28 import com.intellij.openapi.project.Project; 29 import com.intellij.openapi.ui.DialogWrapper; 30 import com.intellij.openapi.ui.TextFieldWithBrowseButton; 31 import org.apache.commons.lang.StringUtils; 32 import org.jetbrains.annotations.NotNull; 33 import org.jetbrains.annotations.Nullable; 34 import javax.swing.JPanel; 35 import javax.swing.JLabel; 36 import javax.swing.KeyStroke; 37 import javax.swing.JComponent; 38 import java.awt.event.ActionEvent; 39 import java.awt.event.KeyEvent; 40 import java.io.File; 41 import java.io.IOException; 42 import java.io.BufferedReader; 43 import java.io.InputStreamReader; 44 import java.nio.charset.Charset; 45 import java.util.List; 46 import java.util.ArrayList; 47 import java.util.Locale; 48 import java.util.LinkedHashMap; 49 import java.util.concurrent.CompletableFuture; 50 import java.util.concurrent.ForkJoinPool; 51 52 /** 53 * UpdateCheckWizardDialog 54 * 55 * @since 23-04-07 56 */ 57 public class UpdateCheckWizardDialog extends DialogWrapper { 58 private static final String LOG_TAG = UpdateCheckWizardDialog.class.getName(); 59 60 private static final Logger LOGGER = Logger.createLogger(); 61 62 private static final int DELETE = 0; 63 64 private static final int MODEL_CHANGES = 13; 65 66 private static final int SYSTEM_API_CHANGES = 9; 67 68 private static final int PERMISSION_CHANGES = 12; 69 70 private static final int NEW_ERROR_CODE = 6; 71 72 private static final int DEPRECATED_CHANGES = 5; 73 74 private static final int FUNCTION_CHANGES = 16; 75 76 private static final int BEHAVIOR_CHANGE = 17; 77 78 private static final String REPORT_DEPRECATED = "api已废弃"; 79 80 private static final String REPORT_NEW_ERROR_CODE = "api新增(错误码)"; 81 82 private static final String REPORT_PERMISSION_CHANGE = "api权限有变化"; 83 84 private static final String REPORT_SYSTEM_API_CHANGES = "api访问级别有变化"; 85 86 private static final String REPORT_MODEL_CHANGE = "api模型使用限制变化"; 87 88 private static final String REPORT_CHANGELOG = "api底层行为变更"; 89 90 private static final String REPORT_FUNCTION_CHANGES = "api函数有变化"; 91 92 private static final String REPORT_DELETE = "api删除"; 93 94 private static final int DIFF_ADD_NEW_API = 3; 95 96 private JPanel centerPanel; 97 98 private TextFieldWithBrowseButton textFieldOldSdkPath; 99 100 private TextFieldWithBrowseButton textFieldNewSdkPath; 101 102 private JLabel labelErrorNotice; 103 104 private Project project; 105 106 private String lastDir; 107 108 private String applicationApiType; 109 110 private List<UpdateCheckReportDto> updateCheckReportDtos = new ArrayList<>(); 111 112 private List<CollectApplicationApiDto> deprecatedApiResults = new ArrayList<>(); 113 114 private String newSdkVersion; 115 116 private String oldSdkVersion; 117 118 private String newSdkFilePath; 119 120 private String reportPath; 121 122 private FileUtils fileUtils; 123 124 /** 125 * UpdateCheckWizardDialog 126 * 127 * @param project project 128 */ UpdateCheckWizardDialog(@otNull Project project)129 public UpdateCheckWizardDialog(@NotNull Project project) { 130 super(project, false); 131 this.project = project; 132 this.fileUtils = new FileUtils(); 133 this.getNewSdkFilePath(); 134 this.setTitle(ConstString.get("check.dialog.title")); 135 this.textFieldNewSdkPath.setText(this.newSdkFilePath); 136 this.applicationApiType = FileUtils.getApplicationApiType(project.getBasePath()); 137 this.initEventListeners(); 138 this.lastDir = FileUtils.getLastDir(); 139 this.init(); 140 } 141 142 /** 143 * UpdateCheckWizardDialog 144 * 145 * @param project project 146 * @param canBeParent canBeParent 147 * @param okAction okAction 148 */ UpdateCheckWizardDialog(@ullable Project project, boolean canBeParent, @Nullable Runnable okAction)149 protected UpdateCheckWizardDialog(@Nullable Project project, boolean canBeParent, @Nullable Runnable okAction) { 150 super(project, canBeParent); 151 } 152 initEventListeners()153 private void initEventListeners() { 154 this.textFieldNewSdkPath.addBrowseFolderListener(ConstString.get("check.dir.path"), 155 ConstString.get("check.dir.path"), 156 this.project, 157 new FileChooserDescriptor(false, true, false, false, false, false) 158 ); 159 160 this.textFieldOldSdkPath.addBrowseFolderListener(ConstString.get("check.old.dir.path"), 161 ConstString.get("check.old.dir.path"), 162 this.project, 163 new FileChooserDescriptor(false, true, false, false, false, false) 164 ); 165 this.centerPanel.registerKeyboardAction(this::onCancel, 166 KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 167 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 168 } 169 getNewSdkFilePath()170 private void getNewSdkFilePath() { 171 try { 172 String sdkDir = FileUtils.getNodePath(this.project.getBasePath(), "local.properties", "sdk.dir"); 173 if (sdkDir == null) { 174 return; 175 } 176 String compileSdkVersion = FileUtils.getCompileSdkVersion(this.project.getBasePath()); 177 if ("null".equals(compileSdkVersion)) { 178 this.labelErrorNotice.setText(ConstString.get("check.unable.to.obtain.sdk")); 179 return; 180 } 181 File file = new File(sdkDir, compileSdkVersion + ConstString.get("check.ets")); 182 this.newSdkFilePath = file.getPath(); 183 } catch (IOException e) { 184 LOGGER.error(LOG_TAG, "Get new sdk file path error! " + e.getMessage()); 185 } 186 } 187 process()188 private void process() { 189 190 // decompression tools 191 this.fileUtils.getApiTools(); 192 193 // get old and new sdk version 194 this.getSdkVersion(); 195 196 // run api tools asynchronously 197 CompletableFuture.allOf(CompletableFuture.runAsync(this::runApiDiffTool, 198 ForkJoinPool.commonPool()), 199 CompletableFuture.runAsync(this::runApiCollectTool, 200 ForkJoinPool.commonPool())).join(); 201 202 // process and filter data 203 this.processAndFilterData(); 204 205 // updateReport Save to disk 206 this.saveReportToDisk(); 207 } 208 saveReportToDisk()209 private void saveReportToDisk() { 210 LOGGER.info(LOG_TAG, "Start save report to disk"); 211 String reportStr = JSON.toJSONString(this.updateCheckReportDtos); 212 FileUtils.writerJsonToFile(reportStr, this.reportPath + ConstString.get("check.report")); 213 LOGGER.info(LOG_TAG, "Save report to disk end"); 214 } 215 runApiDiffTool()216 private void runApiDiffTool() { 217 try { 218 LOGGER.info(LOG_TAG, "Start run api diff tool"); 219 // is it cached 220 File diffResultPath = new File(this.lastDir + ConstString.get("check.diff.result") 221 + "diff(" + this.oldSdkVersion + "_" + this.newSdkVersion + ")" + ".json"); 222 if (diffResultPath.exists()) { 223 LOGGER.info(LOG_TAG, "diff result already exists"); 224 return; 225 } 226 File resultPath = new File(this.lastDir + ConstString.get("check.diff.result")); 227 if (!resultPath.exists()) { 228 resultPath.mkdirs(); 229 } 230 String orders = "node api-diff.js --old " + this.textFieldOldSdkPath.getText() + " --new " + 231 this.newSdkFilePath + " --oldVersion " + this.oldSdkVersion + " --newVersion " + 232 this.newSdkVersion + " --output " + this.lastDir + 233 ConstString.get("check.format"); 234 LOGGER.info(LOG_TAG, "diff order:" + orders); 235 ProcessBuilder builder = new ProcessBuilder(); 236 builder.directory(new File(FileUtils.getLastDir().split(":")[0] + ":\\updateCheck\\api-diff")); 237 builder.command("cmd.exe", "/c", orders); 238 Process start = builder.start(); 239 BufferedReader bufferedReader = new BufferedReader( 240 new InputStreamReader(start.getInputStream(), Charset.forName("GBK"))); 241 String line; 242 while ((line = bufferedReader.readLine()) != null) { 243 System.out.println(line); 244 } 245 LOGGER.info(LOG_TAG, "Run api diff tool end"); 246 } catch (IOException e) { 247 LOGGER.error(LOG_TAG, "Run api diff tool error! " + e.getMessage()); 248 } 249 } 250 runApiCollectTool()251 private void runApiCollectTool() { 252 try { 253 LOGGER.info(LOG_TAG, "Start run api collect tool"); 254 File updateCheck = new File(this.project.getBasePath(), "updateCheck"); 255 this.reportPath = updateCheck.toString(); 256 String orders = "node api-collector.js --app " + this.project.getBasePath() + 257 " --output " + updateCheck + " --sdk " + 258 this.textFieldOldSdkPath.getText() + " --format json"; 259 ProcessBuilder builder = new ProcessBuilder(); 260 builder.directory(new File(FileUtils.getLastDir().split(":")[0] + 261 ":\\updateCheck\\collect_application_api")); 262 LOGGER.info(LOG_TAG, "application order:" + orders); 263 builder.command("cmd.exe", "/c", orders); 264 Process start = builder.start(); 265 BufferedReader bufferedReader = new BufferedReader( 266 new InputStreamReader(start.getInputStream(), Charset.forName("GBK"))); 267 String line; 268 while ((line = bufferedReader.readLine()) != null) { 269 System.out.println(line); 270 } 271 LOGGER.info(LOG_TAG, "Run api collect tool end"); 272 } catch (IOException e) { 273 LOGGER.error(LOG_TAG, "Run api collect tool error! " + e.getMessage()); 274 } 275 } 276 getApiDiffResult()277 private List<ApiDiffResultDto> getApiDiffResult() { 278 List<ApiDiffResultDto> apiDiffResults = new ArrayList<>(); 279 try { 280 String diffJsonFilePath = this.lastDir + ConstString.get("check.diff.path") + 281 this.oldSdkVersion + "_" + this.newSdkVersion + ").json"; 282 apiDiffResults = FileUtils.readJsonFileToJavaList(diffJsonFilePath, ApiDiffResultDto.class); 283 return apiDiffResults; 284 } catch (IOException e) { 285 LOGGER.error(LOG_TAG, "Get api diff result error! " + e.getMessage()); 286 return apiDiffResults; 287 } 288 } 289 getAllCollectApiResult()290 private List<CollectApplicationApiDto> getAllCollectApiResult() { 291 List<CollectApplicationApiDto> collectApplicationApiDtos = new ArrayList<>(); 292 try { 293 File allApiJsonFilePath = new File(this.reportPath, "collectedApi.json"); 294 collectApplicationApiDtos = FileUtils.readJsonFileToJavaList(allApiJsonFilePath.toString(), 295 CollectApplicationApiDto.class); 296 if (collectApplicationApiDtos == null) { 297 return new ArrayList<>(); 298 } 299 for (CollectApplicationApiDto allApi : collectApplicationApiDtos) { 300 if (StringUtils.isNotBlank(allApi.getDeprecated())) { 301 this.deprecatedApiResults.add(allApi); 302 } 303 } 304 return collectApplicationApiDtos; 305 } catch (IOException e) { 306 LOGGER.error(LOG_TAG, "Get collect api result error! " + e.getMessage()); 307 return collectApplicationApiDtos; 308 } 309 } 310 processAndFilterData()311 private void processAndFilterData() { 312 LOGGER.info(LOG_TAG, "Start process and filter data"); 313 List<ApiDiffResultDto> apiDiffResultDtos = this.getApiDiffResult(); 314 List<CollectApplicationApiDto> allApiResult = this.getAllCollectApiResult(); 315 List<Integer> subscript = new ArrayList<>(); 316 317 // filter data 318 for (ApiDiffResultDto apiDto : apiDiffResultDtos) { 319 if (apiDto.getStatusCode() == null || apiDto.getStatusCode() == DIFF_ADD_NEW_API) { 320 continue; 321 } 322 if (apiDto.getStatusCode() == BEHAVIOR_CHANGE) { 323 UpdateCheckReportDto updateCheckReportDto = new UpdateCheckReportDto(); 324 updateCheckReportDto.setPos(ConstString.get("not.involved")); 325 updateCheckReportDto.setReminderInformation( 326 ConstString.get("underlying.behavior")); 327 updateCheckReportDto.setApiDefinition(ConstString.get("not.involved")); 328 updateCheckReportDto.setSourceFileName(ConstString.get("not.involved")); 329 updateCheckReportDto.setChangelogs(apiDto.getChangelogs()); 330 updateCheckReportDto.setChangeType(REPORT_CHANGELOG); 331 this.updateCheckReportDtos.add(updateCheckReportDto); 332 continue; 333 } 334 this.handleDeprecatedApi(subscript, apiDto); 335 this.judgeCollDiff(allApiResult, apiDto); 336 } 337 this.addDeprecatedToReport(subscript); 338 LOGGER.info(LOG_TAG, "Process and filter data end"); 339 } 340 judgeCollDiff(List<CollectApplicationApiDto> allApiResult, ApiDiffResultDto apiDto)341 private void judgeCollDiff(List<CollectApplicationApiDto> allApiResult, ApiDiffResultDto apiDto) { 342 for (CollectApplicationApiDto collApiDto : allApiResult) { 343 if (this.judgeApi(apiDto, collApiDto)) { 344 UpdateCheckReportDto reportDtoToCheck = this.generateReportData(apiDto, collApiDto); 345 if (reportDtoToCheck != null) { 346 this.updateCheckReportDtos.add(reportDtoToCheck); 347 } 348 } 349 } 350 } 351 handleDeprecatedApi(List<Integer> subscript, ApiDiffResultDto apiDto)352 private void handleDeprecatedApi(List<Integer> subscript, ApiDiffResultDto apiDto) { 353 for (int i = 0; i < this.deprecatedApiResults.size(); i++) { 354 if (apiDto.getStatusCode() == DEPRECATED_CHANGES || apiDto.getStatusCode() == DELETE) { 355 if (this.judgeApi(apiDto, this.deprecatedApiResults.get(i))) { 356 subscript.add(i); 357 } 358 } 359 } 360 } 361 addDeprecatedToReport(List<Integer> subscript)362 private void addDeprecatedToReport(List<Integer> subscript) { 363 for (int i = 0; i < this.deprecatedApiResults.size(); i++) { 364 if (!subscript.contains(i)) { 365 UpdateCheckReportDto updateCheckReportDto = new UpdateCheckReportDto(); 366 367 // fill data 368 updateCheckReportDto.setApiDefinition(this.deprecatedApiResults.get(i).getApiRawText()); 369 String reminderInformation = 370 (StringUtils.isBlank(this.deprecatedApiResults.get(i).getUseinstead())) 371 ? ConstString.get("obsolete.version.change.to") 372 : ConstString.get("obsolete.version.change.to") + 373 ConstString.get("api.recommended.use") + 374 this.deprecatedApiResults.get(i).getUseinstead(); 375 updateCheckReportDto.setReminderInformation(reminderInformation); 376 updateCheckReportDto.setChangeType(REPORT_DEPRECATED); 377 updateCheckReportDto.setSourceFileName(this.deprecatedApiResults.get(i).getSourceFileName()); 378 updateCheckReportDto.setPos(this.deprecatedApiResults.get(i).getPos()); 379 updateCheckReportDto.setChangelogs(null); 380 this.updateCheckReportDtos.add(updateCheckReportDto); 381 } 382 } 383 } 384 judgeApi(ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto)385 private boolean judgeApi(ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto) { 386 return apiDto.getDtsName().equals(collApiDto.getDtsName()) 387 && apiDto.getRawText().equals(collApiDto.getApiRawText()) 388 && apiDto.getClassName().equals(collApiDto.getTypeName()); 389 } 390 generateReportData(ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto)391 private UpdateCheckReportDto generateReportData(ApiDiffResultDto apiDto, 392 CollectApplicationApiDto collApiDto) { 393 UpdateCheckReportDto updateCheckReportDto = null; 394 int statusCode = apiDto.getStatusCode(); 395 String checkOld = convertString(apiDto.getOldMessage()); 396 String checkNew = convertString(apiDto.getNewMessage()); 397 switch (statusCode) { 398 case FUNCTION_CHANGES: 399 updateCheckReportDto = new UpdateCheckReportDto(apiDto, 400 ConstString.get("version.change") + 401 apiDto.getNewMessage(), collApiDto, REPORT_FUNCTION_CHANGES); 402 break; 403 case MODEL_CHANGES: 404 UpdateCheckReportDto report = this.setApiModelChange(apiDto, collApiDto); 405 if (report != null) { 406 updateCheckReportDto = report; 407 } 408 break; 409 case SYSTEM_API_CHANGES: 410 updateCheckReportDto = new UpdateCheckReportDto(apiDto, 411 ConstString.get("level.change.to") + checkOld + 412 ConstString.get("check.change.to") + checkNew, collApiDto, REPORT_SYSTEM_API_CHANGES); 413 break; 414 case PERMISSION_CHANGES: 415 updateCheckReportDto = new UpdateCheckReportDto(apiDto, 416 ConstString.get("authority.change.to") + 417 checkOld + ConstString.get("check.change.to") + checkNew, 418 collApiDto, REPORT_PERMISSION_CHANGE); 419 break; 420 } 421 if (updateCheckReportDto != null) { 422 return updateCheckReportDto; 423 } 424 return this.assignmentReport(statusCode, apiDto, collApiDto); 425 } 426 assignmentReport(int statusCode, ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto)427 private UpdateCheckReportDto assignmentReport(int statusCode, 428 ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto) { 429 UpdateCheckReportDto reportDto = null; 430 String checkNew = convertString(apiDto.getNewMessage()); 431 switch (statusCode) { 432 case NEW_ERROR_CODE: 433 reportDto = new UpdateCheckReportDto(apiDto, 434 ConstString.get("add.error.code") + checkNew, collApiDto, REPORT_NEW_ERROR_CODE); 435 break; 436 case DEPRECATED_CHANGES: 437 String useInstead = apiDto.getHint(); 438 if (StringUtils.isBlank(useInstead)) { 439 reportDto = new UpdateCheckReportDto(apiDto, 440 ConstString.get("obsolete.version.change.to"), collApiDto, REPORT_DEPRECATED); 441 } else { 442 reportDto = new UpdateCheckReportDto(apiDto, 443 ConstString.get("obsolete.version.change.to") + ConstString.get("api.recommended.use") + 444 useInstead.replace("useinstead: ", ""), collApiDto, REPORT_DEPRECATED); 445 } 446 break; 447 case DELETE: 448 reportDto = new UpdateCheckReportDto(apiDto, 449 ConstString.get("check.api.deleted"), collApiDto, REPORT_DELETE); 450 break; 451 } 452 return reportDto; 453 } 454 convertString(String str)455 private String convertString(String str) { 456 return StringUtils.isBlank(str) ? ConstString.get("check.nothing") : str; 457 } 458 setApiModelChange(ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto)459 private UpdateCheckReportDto setApiModelChange(ApiDiffResultDto apiDto, CollectApplicationApiDto collApiDto) { 460 if (StringUtils.isBlank(apiDto.getNewMessage())) { 461 return null; 462 } 463 464 if (apiDto.getNewMessage().toLowerCase().startsWith(this.applicationApiType.toLowerCase(Locale.ROOT))) { 465 return null; 466 } 467 468 String oldModelOnly = StringUtils.isBlank( 469 apiDto.getOldMessage()) 470 ? ConstString.get("check.nothing") 471 : apiDto.getOldMessage(); 472 473 return new UpdateCheckReportDto(apiDto, ConstString.get("check.model.change.to") + 474 oldModelOnly + ConstString.get("check.change.to") + 475 apiDto.getNewMessage(), collApiDto, REPORT_MODEL_CHANGE); 476 477 } 478 readOldSdkPath()479 private File readOldSdkPath() { 480 if (!IoUtils.isValidLocalPath(this.textFieldOldSdkPath.getText())) { 481 this.labelErrorNotice.setText(ConstString.get("check.old.sdk.path")); 482 return null; 483 } 484 if (this.isEtsFilePath(this.textFieldOldSdkPath.getText())) { 485 this.labelErrorNotice.setText(ConstString.get("check.old.sdk.file.path")); 486 return null; 487 } 488 return new File(this.textFieldOldSdkPath.getText()); 489 } 490 readNewSdkPath()491 private File readNewSdkPath() { 492 if (!IoUtils.isValidLocalPath(this.textFieldNewSdkPath.getText())) { 493 this.labelErrorNotice.setText(ConstString.get("check.new.sdk.path")); 494 return null; 495 } 496 if (this.isEtsFilePath(this.textFieldNewSdkPath.getText())) { 497 this.labelErrorNotice.setText(ConstString.get("check.new.sdk.file.path")); 498 return null; 499 } 500 return new File(this.textFieldNewSdkPath.getText()); 501 } 502 getSdkVersion()503 private void getSdkVersion() { 504 this.oldSdkVersion = FileUtils.getSdkVersionFromJsonFile( 505 this.textFieldOldSdkPath.getText() + ConstString.get("check.package")); 506 this.newSdkVersion = FileUtils.getSdkVersionFromJsonFile( 507 this.newSdkFilePath + ConstString.get("check.package")); 508 } 509 510 @Override createCenterPanel()511 protected JComponent createCenterPanel() { 512 return this.centerPanel; 513 } 514 515 @Override doOKAction()516 protected void doOKAction() { 517 File oldSdkPath = this.readOldSdkPath(); 518 if (oldSdkPath == null) { 519 LOGGER.info(LOG_TAG, "old sdk path is null."); 520 return; 521 } 522 523 File newSdkPath = this.readNewSdkPath(); 524 if (newSdkPath == null) { 525 LOGGER.info(LOG_TAG, "new sdk path is null."); 526 return; 527 } 528 super.doOKAction(); 529 ProgressManager.getInstance().runProcessWithProgressSynchronously( 530 this::process, ConstString.get("check.generating.report"), false, this.project); 531 MessageBox.showDialog(this.project, 532 ConstString.get("report.successfully"), 533 ConstString.get("check.view.report")); 534 DataUpdateNotifier 535 .getInstance() 536 .notifyDataChange(new LinkedHashMap<String, Boolean>(), "executeAgain"); 537 } 538 onCancel(@ullable ActionEvent event)539 private void onCancel(@Nullable ActionEvent event) { 540 super.doCancelAction(); 541 dispose(); 542 } 543 isEtsFilePath(String filePath)544 private boolean isEtsFilePath(String filePath) { 545 File apiFile = new File(filePath, "api"); 546 File componentFile = new File(filePath, "component"); 547 return !apiFile.exists() || !componentFile.exists(); 548 } 549 550 @Override doCancelAction()551 public void doCancelAction() { 552 this.onCancel(null); 553 } 554 555 /** 556 * showDialog 557 * 558 * @param project project 559 */ showDialog(Project project)560 public static synchronized void showDialog(Project project) { 561 UpdateCheckWizardDialog updateCheckWizardDialog = new UpdateCheckWizardDialog(project); 562 updateCheckWizardDialog.show(); 563 } 564 565 } 566