├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── kotlinc.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml ├── vcs.xml └── workspace.xml ├── MVPHelper-master.iml ├── MVPHelper.iml ├── MVPHelper_v2_4_release.jar ├── README.md ├── README_EN.md ├── img ├── error_class_name.gif ├── error_package.gif ├── go_home_you_are_drunk.png ├── install_local.png ├── install_repositories.png ├── log.gif ├── mode_contract.gif ├── mode_presenter.gif ├── mvp_contract.gif ├── mvp_presenter.gif ├── step1.png ├── step2.png └── suffix_support.gif ├── resources ├── META-INF │ └── plugin.xml └── string.properties └── src └── com └── wingsofts └── mvphelper ├── action └── MvpHelperAction.java ├── biz ├── EventLogger.java ├── Startable.java ├── checker │ ├── Checker.java │ └── impl │ │ └── EnvironmentChecker.java ├── config │ ├── MvpConfigurable.java │ └── impl │ │ └── MvpConfig.java ├── dir │ └── generator │ │ ├── DirGenerator.java │ │ └── impl │ │ ├── BaseDirGenerator.java │ │ ├── ContractModeDirGenerator.java │ │ └── PresenterModeDirGenerator.java └── file │ └── generator │ ├── FileGenerator.java │ └── impl │ ├── BaseFileGenerator.java │ └── JavaModeFileGenerator.java └── gui ├── MvpHelperConfigPanel.form └── MvpHelperConfigPanel.java /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *If you are creating a new issue, make sure you could provide a piece of `bug report` / `stacktrace` / `screenshot`.* 2 | *It's a little bit hard to fix a bug according to a single sentence* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out/ 2 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 21 | 22 | 32 | 33 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 584 | 585 | 586 | 587 | 593 | 594 | 595 | 607 | 608 | 609 | 610 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 646 | 647 | 648 | 658 | 659 | 660 | 669 | 670 | 676 | 677 | 678 | 683 | 684 | 685 | 690 | 691 | 692 | 697 | 698 | 699 | 704 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 747 | 760 | 761 | 770 | 774 | 775 | 776 | 783 | 786 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | project 845 | 846 | 847 | true 848 | 849 | 850 | 851 | DIRECTORY 852 | 853 | false 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | project 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 878 | 879 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 1473060314115 889 | 903 | 904 | 1493132904948 905 | 910 | 911 | 1493134585805 912 | 917 | 918 | 1493134773863 919 | 924 | 925 | 1493135278957 926 | 931 | 932 | 1493135815003 933 | 938 | 939 | 1493136971558 940 | 945 | 946 | 1493138211169 947 | 952 | 953 | 1493141262689 954 | 959 | 960 | 1493141758926 961 | 966 | 967 | 1493303012124 968 | 973 | 974 | 1493303049251 975 | 980 | 981 | 1493303087063 982 | 987 | 988 | 1493303717653 989 | 994 | 995 | 1493305389977 996 | 1001 | 1002 | 1493305418660 1003 | 1008 | 1009 | 1493305441900 1010 | 1015 | 1016 | 1500469216207 1017 | 1022 | 1023 | 1500469536162 1024 | 1029 | 1030 | 1500469826220 1031 | 1036 | 1037 | 1500471830488 1038 | 1043 | 1044 | 1500472009089 1045 | 1050 | 1051 | 1521637095192 1052 | 1057 | 1058 | 1521637120659 1059 | 1064 | 1067 | 1068 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1105 | 1106 | 1130 | 1131 | 1132 | 1135 | 1138 | 1139 | 1140 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1168 | 1169 | 1170 | 1172 | 1173 | 1174 | 1175 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | 1430 | 1431 | 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | 1442 | 1443 | 1444 | 1445 | 1446 | 1447 | 1448 | 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 | 1485 | 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | 1505 | 1506 | 1507 | 1508 | 1509 | 1510 | 1511 | 1512 | 1513 | 1514 | 1515 | 1516 | 1517 | 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | 1525 | 1526 | 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | 1546 | 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | 1609 | 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | 1644 | 1645 | 1646 | 1647 | 1652 | 1653 | 1654 | 1655 | 1656 | 1657 | No facets are configured 1658 | 1659 | 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | jquery-1.11.1 1670 | 1671 | 1676 | 1677 | 1678 | 1679 | 1680 | 1681 | IntelliJ IDEA IU-173.4674.33 1682 | 1683 | 1688 | 1689 | 1690 | 1691 | 1692 | 1693 | MVPHelper 1694 | 1695 | 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | IntelliJ IDEA Community Edition IC-162.1121.32 1707 | 1708 | 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1724 | 1725 | 1726 | 1727 | 1728 | 1729 | -------------------------------------------------------------------------------- /MVPHelper-master.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MVPHelper.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /MVPHelper_v2_4_release.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/MVPHelper_v2_4_release.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MvpHelper [English Document](/README_EN.md) 2 | 一款适用于 ```Intellij IDEA``` 和 ```Android Studio``` 的插件, 可用于为MVP架构生成接口以及实现类, 解放双手. 3 | 4 | 5 | 欢迎 ```Issue Report```, 欢迎 ```Feature Request```, 欢迎 ```Pull Request``` 6 | 7 | 感谢[XieEDeHeiShou](https://github.com/XieEDeHeiShou) 提供重构后的代码以及特性跟进。 8 | 9 | ## 预览 10 | ```Contract``` 模式: 11 | 12 | ![mode_contract](img/mode_contract.gif) 13 | 14 | ```Presenter``` 模式(已设置后缀Impl): 15 | 16 | ![mode_presenter](img/mode_presenter.gif) 17 | 18 | 后缀支持: 19 | 20 | ![suffix_support](img/suffix_support.gif) 21 | 22 | 日志输出(气球偶尔会闪现一下,可以在Event Log里关闭): 23 | 24 | ![log](img/log.gif) 25 | 26 | 错误提示: 27 | 28 | ![error_package](img/error_package.gif) 29 | 30 | ![error_class_name](img/error_class_name.gif) 31 | 32 | ## 注意 33 | 对于MVP模式,定义一个 ```Contract``` 类来放置 ```Model``` , ```View``` , ```Presenter``` 的接口,将大大减少类文件。 34 | 将普通的接口替代如下: 35 | 36 | public interface GoodsInfoContract { 37 | 38 | interface View { 39 | 40 | } 41 | interface Presenter { 42 | 43 | } 44 | interface Model { 45 | 46 | } 47 | 48 | } 49 | 50 | ## 如何安装 51 | ### 在插件仓库搜索MvpHelper 52 | ![image](img/install_repositories.png) 53 | 54 | ### 或者 55 | #### 第一步 56 | 下载[MvpHelper_v2_3_release.jar](MVPHelper_v2_4_release.jar) 57 | 或者前往 [releases](https://github.com/XieEDeHeiShou/MVPHelper/releases/latest) 58 | 59 | #### 第二步 60 | 安装插件 61 | 62 | ![image](img/install_local.png) 63 | 64 | ## 如何使用 65 | 在 ```Contract``` 类或者 ```Presenter``` 类内部,点击 ```Generate``` 菜单或使用快捷键 ```Alt + Insert```, 66 | 选择 ```Mvp Helper``` 即可生成对应文件. 67 | 68 | ## 已知特性 69 | + 移除快捷键 ```Meta + 1``` 70 | + 在非Java源文件中使用快捷键 ```Alt + Insert``` 时, MvpHelper菜单将不再出现 71 | + 不再限制根目录包名 72 | + 不再将 ```contract``` 包限制在根目录下, 并且支持sub-package 73 | 例如: 74 | >```com.example.project.package1.contract.package2.AContract``` 75 | 将会生成 ```com.example.project.package1.model.package2.AModel``` 76 | 以及 ```com.example.project.package1.presenter.package2.APresenter``` 77 | + 当 ```AContract``` 位于 ```presenter``` 包下时将会出现错误提示而非继续生成错误的类 78 | + 支持File Header Template 79 | + 已实现 ```Contract``` 模式 80 | + ```Contract``` 所处的位置中必须有至少一个包以 ```contract``` 结尾 81 | + 强制 ```Contract``` 为 ```interface``` 82 | + ```Contract``` 中的三个接口不包含多余的修饰符 83 | + 重复生成将删除旧文件并重新生成 84 | + 已实现 ```Presenter``` 模式 85 | + ```Presenter``` 所处的位置中必须有至少一个包以 ```presenter``` 结尾 86 | + 在 ```Presenter``` 中启动插件将复刻 ```Contract``` 模式下的实现方式 87 | + ```Presenter``` 模式在自定义后缀的情况下不会删除原有 ```Presenter```, 因此我们建议使用```Contract``` 模式 88 | 89 | ## TODO 90 | + 实现 ```Activity``` 模式 91 | + 实现 ```Fragment``` 模式 92 | + 将生成的文件自动添加到Git (考虑到要进一步学习Git的命令行语法等相关内容, 可能会鸽) 93 | 94 | ## 参考资源 95 | + [Intellij Platform SDK DevGuide](http://www.jetbrains.org/intellij/sdk/docs/) 96 | + [Persisting State of Components](http://www.jetbrains.org/intellij/sdk/docs/basics/persisting_state_of_components.html) 97 | How to save and load plugin configs 98 | + [Customizing the IDEA Settings Dialog](https://confluence.jetbrains.com/display/IDEADEV/Customizing+the+IDEA+Settings+Dialog) 99 | + [IDEA Online Source Code](https://upsource.jetbrains.com/idea-ce) 100 | + [CreateClassAction](https://upsource.jetbrains.com/idea-ce/file/idea-ce-10df87d7a9840e5901d4901ac4fff7ba035501c2/java/java-impl/src/com/intellij/ide/actions/CreateClassAction.java) 101 | + [ApplicationConfigurable, ProjectConfigurable](http://corochann.com/intellij-plugin-development-introduction-applicationconfigurable-projectconfigurable-873.html) 102 | + [PersistStateComponent](http://corochann.com/intellij-plugin-development-introduction-persiststatecomponent-903.html) 103 | + [Java Api Sample](http://www.programcreek.com/java-api-examples/index.php) 104 | + [Moxun's bolg](https://moxun.me/archives/category/黑科技/idea插件开发) (中文博客) 105 | + [AndroidStudio/IDEA插件开发学习](http://www.jianshu.com/p/0117d4b1eb00) (中文博客) 106 | 107 | ## 更新日志 108 | ### v2.x版 -基于Intellij Open Api 实现 by [XieEDeHeiShou](https://github.com/XieEDeHeiShou) 109 | + 2017-07-19 110 | + 修复潜在的NPE异常(#20) 111 | + MvpHelper_v2_3_release.jar 112 | + 2017-04-27 113 | + 修复实现类修饰符异常 package-private -> public 114 | + 新增日志类EventLogger 115 | + MvpHelper_v2_2_release.jar 116 | + 2017-04-25 117 | + 实现配置信息的存取 118 | + 实现运行时加载配置信息 119 | + MvpHelper_v2_0_release.jar 120 | + 追加 用户指导.gif 121 | + 向[原仓库](https://github.com/githubwing/MVPHelper)请求合并 122 | + 在Android Studio 环境下测试Java项目与Android项目 123 | + 修复Merge后造成的 ```Contract``` 修饰符异常,强制生成后的 ```Contract``` 为 ```public interface PrefixContract``` 124 | + 修复配置面板在销毁时触发的 ```AbstractMethodException``` 125 | + MvpHelper_v2_1_release.jar 126 | + 2017-04-24 127 | + 实现配置面板布局 128 | + 实现 ```Presenter``` 模式下的包生成类 ```PresenterModeDirGenerator``` 129 | + 抽取各功能实现类接口,重新分包 130 | + 基于Intellij Open Api 更新 ```Contract``` 而非以替换文本的方式更新 131 | + 强制 ```Contract``` 为 ```interface``` 132 | + 移除 ```Contract``` 中接口的多余修饰符 ```public``` 133 | + 2017-04-23 134 | + 实现在指定包下生成类文件, 并注入包信息 135 | + 实现让生成类实现特定接口 136 | + 更新README.CN.MD 137 | + 2017-04-15 更新对当前环境的判定规则 138 | + 2017-04-12 实现Action的可见性判定, 当用户尝试通过 ```Alt + Insert``` 启动插件时, 139 | 将判断当前环境是否为Java源文件环境, 当条件不成立时将不再显示插件动作 140 | + 2017-04-11 实现 ```DirGenerator``` 与 ```FileGenerator``` 用于分别生成 ```package``` 与 ```class``` 141 | + 2017-04-10 实现 ```EnvironmentChecker``` 用于检查当前环境是否满足生成条件: 142 | + Java源文件的文件名是否以 ```Contract``` 结尾, 并处于 ```contract``` 包下 143 | + Java源文件的文件名是否以 ```Presenter``` 结尾, 并处于 ```presenter``` 包下 144 | 145 | 146 | ### v1.x版 -基于Java IO Api 实现 by [githubwing](https://github.com/githubwing) 147 | + 2017-04-10 将源文件分包并尝试重构 148 | + 2017-04-09 移除源文件中的 ```C``` 语言异味 149 | + 2017-04-06 分支自[原仓库](https://github.com/githubwing/MVPHelper), 并向源文件中追加注释, 以便阅读 150 | 151 | ## Eclipse 152 | ![GO HOME](./img/go_home_you_are_drunk.png) 153 | 154 | # License 155 | 156 | Copyright 2016 androidwing1992 157 | 158 | Licensed under the Apache License, Version 2.0 (the "License"); 159 | you may not use this file except in compliance with the License. 160 | You may obtain a copy of the License at 161 | 162 | http://www.apache.org/licenses/LICENSE-2.0 163 | 164 | Unless required by applicable law or agreed to in writing, software 165 | distributed under the License is distributed on an "AS IS" BASIS, 166 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 167 | See the License for the specific language governing permissions and 168 | limitations under the License. 169 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # NOTE THIS IS A TRANSLATED COPY OF [中文文档](/README.md), MAY OUT OF DATE. 2 | 3 | Thanks for [XieEDeHeiShou](https://github.com/XieEDeHeiShou) 4 | # MvpHelper 5 | A plugin that designed for ```Intellij IDEA``` and ```Android Studio```, used to generate Mvp interfaces and classes. 6 | 7 | ```Issue Report``` welcomed, ```Feature Request``` welcomed, ```Pull Request``` welcomed 8 | 9 | ## Preview 10 | ```Contract``` mode: 11 | 12 | ![mode_contract](img/mode_contract.gif) 13 | 14 | ```Presenter``` mode(with suffix 'Impl'): 15 | 16 | ![mode_presenter](img/mode_presenter.gif) 17 | 18 | Suffix support: 19 | 20 | ![suffix_support](img/suffix_support.gif) 21 | 22 | Log (unexpected balloon may appear, just disable it in the Event Log): 23 | ![log](img/log.gif) 24 | 25 | Error dialog: 26 | 27 | ![error_package](img/error_package.gif) 28 | 29 | ![error_class_name](img/error_class_name.gif) 30 | 31 | ## Note 32 | For Mvp, define an interface ```Contract``` to place ```Model``` , ```View``` , ```Presenter``` 33 | will greatly reduce the number of source files: 34 | 35 | public interface GoodsInfoContract { 36 | 37 | interface View { 38 | 39 | } 40 | interface Presenter { 41 | 42 | } 43 | interface Model { 44 | 45 | } 46 | 47 | } 48 | 49 | ## How to install 50 | ### Search in the repositories with keyword:MvpHelper 51 | ![image](img/install_repositories.png) 52 | 53 | ### Or install plugin.jar 54 | #### step1 55 | Download [MvpHelper_v2_3_release.jar](MVPHelper_v2_4_release.jar) 56 | or redirect to [releases](https://github.com/XieEDeHeiShou/MVPHelper/releases/latest) 57 | 58 | #### step2 59 | Install it. 60 | 61 | ![image](img/install_local.png) 62 | 63 | ## How to use 64 | In ```Contract``` or ```Presenter``` ,click ```Generate``` menu or try hot-key ```Alt + Insert```, 65 | select ```Mvp Helper``` . 66 | 67 | ## Feature 68 | + Remove hot-key ```Meta + 1``` 69 | + MvpHelper menu will not appear in un-java files when use hot key ```Alt + Insert``` 70 | + No more root package limit (v1.0 only support com,org,me...) 71 | + No more ```contract``` package location limit, and support sub-package: 72 | >```com.example.project.package1.contract.package2.AContract``` 73 | will generate ```com.example.project.package1.model.package2.AModel``` 74 | and ```com.example.project.package1.presenter.package2.APresenter``` 75 | + While ```AContract``` under ```presenter``` package, an error dialog will occur.(used to generate wrong files) 76 | + Support File Header Template 77 | + ```Contract``` mode achieved 78 | + ```Contract``` MUST under a path witch has at least one package END WITH ```contract``` 79 | + Force ```Contract``` to be a ```public interface``` 80 | + Remove unnecessary modifier form inner interfaces of ```Contract``` 81 | + Re-generate will delete old files then generate new files. 82 | + ```Presenter``` mode achieved 83 | + ```Presenter``` MUST under a path witch has at least one package END WITH ```presenter``` 84 | + When launch with```Presenter``` mode, it will call ```Contract``` mode later. 85 | + When launch with```Presenter``` mode with custom suffix, the old ```Presenter```, which without suffix, 86 | will not be delete automatically. So we recommend ```Contract``` mode. 87 | ## TODO 88 | + Achieve ```Activity``` mode 89 | + Achieve ```Fragment``` mode 90 | + Add generated files to git automatically (may take long time to learn command grammar of git) 91 | 92 | ## Related reference 93 | + [Intellij Platform SDK DevGuide](http://www.jetbrains.org/intellij/sdk/docs/) 94 | + [Persisting State of Components](http://www.jetbrains.org/intellij/sdk/docs/basics/persisting_state_of_components.html) 95 | How to save and load plugin configs 96 | + [Customizing the IDEA Settings Dialog](https://confluence.jetbrains.com/display/IDEADEV/Customizing+the+IDEA+Settings+Dialog) 97 | + [IDEA Online Source Code](https://upsource.jetbrains.com/idea-ce) 98 | + [CreateClassAction](https://upsource.jetbrains.com/idea-ce/file/idea-ce-10df87d7a9840e5901d4901ac4fff7ba035501c2/java/java-impl/src/com/intellij/ide/actions/CreateClassAction.java) 99 | + [ApplicationConfigurable, ProjectConfigurable](http://corochann.com/intellij-plugin-development-introduction-applicationconfigurable-projectconfigurable-873.html) 100 | + [PersistStateComponent](http://corochann.com/intellij-plugin-development-introduction-persiststatecomponent-903.html) 101 | + [Java Api Sample](http://www.programcreek.com/java-api-examples/index.php) 102 | + [Moxun's bolg](https://moxun.me/archives/category/黑科技/idea插件开发) (Chinese blog) 103 | + [AndroidStudio/IDEA插件开发学习](http://www.jianshu.com/p/0117d4b1eb00) (Chinese blog) 104 | 105 | ## Change log 106 | ### v2.x -Based on Intellij Open Api, by [XieEDeHeiShou](https://github.com/XieEDeHeiShou) 107 | + 2017-07-19 108 | + Fix NPE(#20) 109 | + MvpHelper_v2_3_release.jar 110 | + 2017-04-27 111 | + Fix generated class modifier bug:package-private -> public 112 | + Add EventLogger 113 | + MvpHelper_v2_2_release.jar 114 | + 2017-04-25 115 | + Save and load configurations. 116 | + Load config while running. 117 | + MvpHelper_v2_0_release.jar 118 | + Add guide.gif 119 | + Pull request to [Original Repo](https://github.com/githubwing/MVPHelper) 120 | + Test under Android Studio Environment with Java project and Android project 121 | + Fix bug caused by merge: generated ```Contract``` lose ```public``` modifier, force to be ```public interface PrefixContract``` 122 | + Fix ```AbstractMethodException``` caused by platform resource release 123 | + MvpHelper_v2_1_release.jar 124 | + 2017-04-24 125 | + Layout config panel 126 | + Achieve ```PresenterModeDirGenerator``` 127 | + Extract interface, re-package 128 | + Change the way to update ```Contract``` 129 | + force ```Contract``` to be ```interface``` 130 | + Remove unnecessary modifier form inner interfaces of ```Contract``` 131 | + 2017-04-23 132 | + Achieve file generation , package inject, support File Head Template 133 | + Achieve generated class implement special interface 134 | + Update README.CN.MD(Removed later) 135 | + 2017-04-15 Update Environment check rule 136 | + 2017-04-12 Achieve plugin visibility control, when launched by hot-key ```Alt + Insert``` 137 | ,if current file is not a javaFile.java, the plugin menu will be invisible. 138 | + 2017-04-11 Achieve ```DirGenerator``` and ```FileGenerator``` used to generate dirs and files 139 | + 2017-04-10 Achieve ```EnvironmentChecker``` used to check plugin runtime condition: 140 | + Is the document END WITH ```Contract``` and UNDER a ```contract``` package 141 | + Is the document END WITH ```Presneter``` and UNDER a ```presenter``` package 142 | 143 | ### v1.x -Based on Java IO Api, by [githubwing](https://github.com/githubwing) 144 | + 2017-04-10 Try to rebuild the project with Intellij Open Api. 145 | + 2017-04-09 Remove ```C``` smell form original source code. 146 | + 2017-04-06 Fork form [original repe](https://github.com/githubwing/MVPHelper), add comment lines for reading. 147 | 148 | ## Eclipse 149 | ![GO HOME](./img/go_home_you_are_drunk.png) 150 | 151 | # License 152 | 153 | Copyright 2016 androidwing1992 154 | 155 | Licensed under the Apache License, Version 2.0 (the "License"); 156 | you may not use this file except in compliance with the License. 157 | You may obtain a copy of the License at 158 | 159 | http://www.apache.org/licenses/LICENSE-2.0 160 | 161 | Unless required by applicable law or agreed to in writing, software 162 | distributed under the License is distributed on an "AS IS" BASIS, 163 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 164 | See the License for the specific language governing permissions and 165 | limitations under the License. 166 | -------------------------------------------------------------------------------- /img/error_class_name.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/error_class_name.gif -------------------------------------------------------------------------------- /img/error_package.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/error_package.gif -------------------------------------------------------------------------------- /img/go_home_you_are_drunk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/go_home_you_are_drunk.png -------------------------------------------------------------------------------- /img/install_local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/install_local.png -------------------------------------------------------------------------------- /img/install_repositories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/install_repositories.png -------------------------------------------------------------------------------- /img/log.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/log.gif -------------------------------------------------------------------------------- /img/mode_contract.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/mode_contract.gif -------------------------------------------------------------------------------- /img/mode_presenter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/mode_presenter.gif -------------------------------------------------------------------------------- /img/mvp_contract.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/mvp_contract.gif -------------------------------------------------------------------------------- /img/mvp_presenter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/mvp_presenter.gif -------------------------------------------------------------------------------- /img/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/step1.png -------------------------------------------------------------------------------- /img/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/step2.png -------------------------------------------------------------------------------- /img/suffix_support.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubwing/MVPHelper/0119edea1bc1b78379050cdfb2523cb1c6a9641c/img/suffix_support.gif -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.wingsofts.plugin.MVPHelper 3 | 4 | 5 | 6 | MVPHelper 7 | 2.4 8 | 9 | 11 | 14 | 15 | A plugin that helps you to create classes and interfaces for MVP. 16 | Click 'Generate' --> 'Mvp Helper' in Contract or Presenter class. 17 | For more detail see the link below. 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | -------------------------------------------------------------------------------- /resources/string.properties: -------------------------------------------------------------------------------- 1 | suffixTip=Whether implementation class's name has this suffix 2 | enableSuffix=Enable Suffix 3 | panelTitle=Mvp Helper Config Panel 4 | suffixExample=HelloWorldModel 5 | feedBackToGitHub=Feed back 6 | feedBackUrl=https://github.com/githubwing/MVPHelper/issues 7 | openOnGitHub=GitHub 8 | gitHubUrl=https://github.com/githubwing/MVPHelper 9 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/action/MvpHelperAction.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.action; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.CommonDataKeys; 6 | import com.intellij.openapi.actionSystem.Presentation; 7 | import com.intellij.openapi.editor.Editor; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.ui.Messages; 10 | import com.intellij.psi.PsiFile; 11 | import com.intellij.psi.PsiJavaFile; 12 | import com.wingsofts.mvphelper.biz.checker.Checker; 13 | import com.wingsofts.mvphelper.biz.checker.impl.EnvironmentChecker; 14 | 15 | /** 16 | * The entry of this plugin. 17 | *

18 | * If you want to contribute to this myProject, please follow 19 | * official tutorial.
20 | * Online source code is also available.
21 | * 22 | * CreateClassAction 23 | *

24 | * 25 | * @author DengChao 26 | * @since 2017/4/9 27 | */ 28 | public class MvpHelperAction extends AnAction { 29 | 30 | @Override 31 | public void update(AnActionEvent event) {//before actionPerformed(AnActionEvent) 32 | super.update(event); 33 | //Set visibility only in case of existing project and editor 34 | Editor editor = event.getData(CommonDataKeys.EDITOR); 35 | Project project = event.getData(CommonDataKeys.PROJECT); 36 | //Set visibility only in '*.java' files. 37 | PsiFile psiFile = event.getData(CommonDataKeys.PSI_FILE); 38 | 39 | Presentation presentation = event.getPresentation(); 40 | if (editor != null && project != null && psiFile instanceof PsiJavaFile) { 41 | presentation.setEnabledAndVisible(true); 42 | } else { 43 | presentation.setEnabledAndVisible(false); 44 | } 45 | } 46 | 47 | @Override 48 | public void actionPerformed(AnActionEvent event) { 49 | Checker checker = new EnvironmentChecker(event); 50 | if (!checker.hasSuffix()) { 51 | Messages.showErrorDialog( 52 | "Generation failed, " + 53 | "your class name MUST END WITH 'Contract' or 'Presenter'.", 54 | "Class Name Error"); 55 | return; 56 | } else if (!checker.isInRightPlace()) { 57 | Messages.showErrorDialog( 58 | "You didn't place the 'Contract' or 'Presenter' under a right package.", 59 | "Package Error"); 60 | return; 61 | } 62 | checker.start(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/EventLogger.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationType; 5 | import com.intellij.notification.Notifications; 6 | import com.intellij.openapi.ui.popup.Balloon; 7 | 8 | /** 9 | * @author Administrator 10 | * @since 2017/4/27 11 | */ 12 | public class EventLogger { 13 | private static final String GROUP_ID = "Mvp Helper";//The unique group id where "Event Log" could use to group your messages together. 14 | private static final String TITLE = "Mvp Helper Event Log";//The title on Balloon 15 | 16 | /** 17 | * Print log to "Event Log" 18 | */ 19 | public static void log(String msg) { 20 | Notification notification = new Notification(GROUP_ID, TITLE, msg, NotificationType.INFORMATION);//build a notification 21 | //notification.hideBalloon();//didn't work 22 | Notifications.Bus.notify(notification);//use the default bus to notify (application level) 23 | Balloon balloon = notification.getBalloon(); 24 | if (balloon != null) {//fix: #20 潜在的NPE 25 | balloon.hide(true);//try to hide the balloon immediately. 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/Startable.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz; 2 | 3 | /** 4 | * @author DengChao 5 | * @since 2017/4/24. 6 | */ 7 | public interface Startable { 8 | /** 9 | * start everything =) 10 | */ 11 | void start(); 12 | } 13 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/checker/Checker.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.checker; 2 | 3 | import com.wingsofts.mvphelper.biz.Startable; 4 | 5 | /** 6 | * The interface witch provide methods to check if current document match all conditions 7 | * 8 | * @author DengChao 9 | * @since 2017/4/10 10 | */ 11 | public interface Checker extends Startable { 12 | /** 13 | * Whether the class name end with 'Contract' or 'Presenter' 14 | */ 15 | boolean hasSuffix(); 16 | 17 | /** 18 | * Whether the document is in a proper package; 19 | */ 20 | boolean isInRightPlace(); 21 | 22 | /** 23 | * Start generation 24 | */ 25 | void start(); 26 | 27 | /** 28 | * The runtime mode. 29 | */ 30 | enum Mode { 31 | CONTRACT, PRESENTER, ACTIVITY, FRAGMENT 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/checker/impl/EnvironmentChecker.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.checker.impl; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.CommonDataKeys; 5 | import com.intellij.psi.PsiJavaFile; 6 | import com.wingsofts.mvphelper.biz.checker.Checker; 7 | import com.wingsofts.mvphelper.biz.dir.generator.DirGenerator; 8 | import com.wingsofts.mvphelper.biz.dir.generator.impl.ContractModeDirGenerator; 9 | import com.wingsofts.mvphelper.biz.dir.generator.impl.PresenterModeDirGenerator; 10 | 11 | /** 12 | * The class used to check generation conditions and start file generation. 13 | * 14 | * @author Administrator 15 | * @since 2017/4/9 16 | */ 17 | @SuppressWarnings("ConstantConditions") 18 | public class EnvironmentChecker implements Checker { 19 | private static final String CONTRACT = "Contract"; 20 | private static final String PRESENTER = "Presenter"; 21 | private final AnActionEvent actionEvent; 22 | private String className = "";//Never NPE, =) 23 | private String packageName = ""; 24 | private Mode mode; 25 | private String prefix; 26 | 27 | public EnvironmentChecker(AnActionEvent actionEvent) { 28 | this.actionEvent = actionEvent; 29 | PsiJavaFile javaFile = (PsiJavaFile) actionEvent.getData(CommonDataKeys.PSI_FILE); 30 | packageName = javaFile.getPackageName(); 31 | String fileName = javaFile.getName(); 32 | className = fileName.substring(0, fileName.indexOf(".java")); 33 | } 34 | 35 | @Override 36 | public boolean hasSuffix() { 37 | if (className.endsWith(CONTRACT)) { 38 | mode = Mode.CONTRACT; 39 | prefix = className.replace(CONTRACT, ""); 40 | return true; 41 | } else if (className.endsWith(PRESENTER)) { 42 | mode = Mode.PRESENTER; 43 | prefix = className.replace(PRESENTER, ""); 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | @Override 50 | public boolean isInRightPlace() {//split packageName by '.' 51 | String[] subPackages = packageName.split("\\."); 52 | if (mode == Mode.CONTRACT) { 53 | for (String subPackage : subPackages) { 54 | if (subPackage.endsWith("contract")) { 55 | return true; 56 | } 57 | } 58 | } else if (mode == Mode.PRESENTER) { 59 | for (String subPackage : subPackages) { 60 | if (subPackage.endsWith("presenter")) { 61 | return true; 62 | } 63 | } 64 | }//A Presenter-class not in presenter-package 65 | return false; 66 | } 67 | 68 | @Override 69 | public void start() { 70 | DirGenerator dirGenerator; 71 | if (mode == Mode.CONTRACT) { 72 | dirGenerator = new ContractModeDirGenerator(actionEvent, prefix); 73 | } else {// mode == Mode.PRESENTER 74 | dirGenerator = new PresenterModeDirGenerator(actionEvent, prefix); 75 | } 76 | dirGenerator.start(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/config/MvpConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.config; 2 | 3 | /** 4 | * @author Administrator 5 | * @since 2017/4/25. 6 | */ 7 | public interface MvpConfigurable { 8 | boolean hasSuffix(); 9 | 10 | String getSuffix(); 11 | } 12 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/config/impl/MvpConfig.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.config.impl; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.wingsofts.mvphelper.biz.config.MvpConfigurable; 5 | import com.wingsofts.mvphelper.gui.MvpHelperConfigPanel; 6 | 7 | /** 8 | * @author Administrator 9 | * @since 2017/4/25. 10 | */ 11 | public class MvpConfig implements MvpConfigurable { 12 | 13 | private final MvpConfigurable configurable; 14 | 15 | public MvpConfig(Project project) { 16 | configurable = new MvpHelperConfigPanel(project, false); 17 | } 18 | 19 | @Override 20 | public boolean hasSuffix() { 21 | return configurable.hasSuffix(); 22 | } 23 | 24 | @Override 25 | public String getSuffix() { 26 | return configurable.getSuffix(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/dir/generator/DirGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.dir.generator; 2 | 3 | import com.wingsofts.mvphelper.biz.Startable; 4 | 5 | /** 6 | * @author Administrator 7 | * @since 2017/4/10 8 | */ 9 | public interface DirGenerator extends Startable { 10 | } 11 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/dir/generator/impl/BaseDirGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.dir.generator.impl; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.CommonDataKeys; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.command.WriteCommandAction; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.ui.Messages; 9 | import com.intellij.psi.PsiDirectory; 10 | import com.intellij.psi.PsiJavaFile; 11 | import com.wingsofts.mvphelper.biz.dir.generator.DirGenerator; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.ResourceBundle; 15 | 16 | import static com.wingsofts.mvphelper.biz.EventLogger.log; 17 | 18 | /** 19 | * @author DengChao 20 | * @see #moveDirPointer(PsiDirectory, String) 21 | * @see #generateDirsBasedOnSuffix(String) 22 | * @see #onGenerateForkDirs(String) 23 | * @since 2017/4/24. 24 | */ 25 | @SuppressWarnings({"ConstantConditions", "WeakerAccess"}) 26 | abstract class BaseDirGenerator implements DirGenerator { 27 | protected final Project myProject; 28 | protected final String myPackageName;//where the action happens 29 | protected PsiDirectory myCurrentDir;//the base line of dir generation 30 | protected PsiDirectory myContractDir; 31 | protected PsiDirectory myModelDir; 32 | protected PsiDirectory myPresenterDir; 33 | protected String myPrefix; 34 | 35 | BaseDirGenerator(@NotNull AnActionEvent actionEvent, @NotNull String prefix) { 36 | this.myPrefix = prefix; 37 | myProject = actionEvent.getData(PlatformDataKeys.EDITOR).getProject(); 38 | 39 | PsiJavaFile javaFile = (PsiJavaFile) actionEvent.getData(CommonDataKeys.PSI_FILE); 40 | myPackageName = javaFile.getPackageName(); 41 | locateRootDir(javaFile.getContainingFile().getParent()); 42 | } 43 | 44 | /** 45 | * Locate the root dir of current project.
46 | * For Android studio project, 'java' folder is always the root of any model by default.
47 | * For IDEA java project, 'src' folder is always the root of any model by default. 48 | * 49 | * @param currentDir where the action happens 50 | */ 51 | private void locateRootDir(PsiDirectory currentDir) { 52 | String currentDirName = currentDir.getName(); 53 | if (currentDirName.equals("java") 54 | || currentDirName.equals("src")) { 55 | myCurrentDir = currentDir; 56 | } else { 57 | PsiDirectory parent = currentDir.getParent(); 58 | if (parent != null) { 59 | locateRootDir(parent);//if this folder is not the root, then try its parent. 60 | } else { 61 | //when there is no more parent, we reached the ROOT of a hard-disk... 62 | //if we still can't locate myCurrentDir by now... 63 | //I guess..not the plugin's fault.. =( 64 | Messages.showErrorDialog( 65 | "I can't imagine what happens to your project," + 66 | " technically, no project could reach here.\n" + 67 | " For your project match the IDEA's 'Java Project' definition," + 68 | " and it match our basic rule: 'Contract' under contract-package and 'Presenter' under presenter-package." + 69 | " Since it does happened, report us the detail please:" + 70 | " image of this dialog, image of your project structure tree, and your description\n" + 71 | ResourceBundle.getBundle("string").getString("feedBackUrl"), 72 | "Locate Root Dir Error"); 73 | throw new RuntimeException("The plugin cannot find a root dir like \"java\" or \"src\""); 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * Make sure to call this on sub-class#{@link #start()}'s first line 80 | * 81 | * @param suffix the suffix of the package witch used to determine where should have a fork. 82 | */ 83 | protected void generateDirsBasedOnSuffix(@NotNull String suffix) { 84 | boolean isForkDirGenerated = false; 85 | String[] subPackages = myPackageName.split("\\."); 86 | for (String subPackage : subPackages) { 87 | if (!subPackage.endsWith(suffix)) {//if the package does not end with 'contract': 88 | if (isForkDirGenerated) {// and the 'presenter' and 'model' dir already generated, 89 | myContractDir = moveDirPointer(myContractDir, subPackage); 90 | myModelDir = moveDirPointer(myModelDir, subPackage); 91 | myPresenterDir = moveDirPointer(myPresenterDir, subPackage); 92 | }// but the 'presenter' and 'model' dir has not been generated, 93 | } else { 94 | onGenerateForkDirs(subPackage); 95 | isForkDirGenerated = true;//update the flag 96 | } 97 | //the current dir is the base line, so 98 | //no matter what happens, move myCurrentDir pointer to it's child; 99 | myCurrentDir = moveDirPointer(myCurrentDir, subPackage); 100 | } 101 | } 102 | 103 | /** 104 | * Move the pointer to it's sub-package.
105 | * sub-package will be generate if it's not exist. 106 | * 107 | * @param subPackage the sub-package's name 108 | */ 109 | @NotNull 110 | protected PsiDirectory moveDirPointer(@NotNull final PsiDirectory currentDir, @NotNull final String subPackage) { 111 | final PsiDirectory[] subDirectory = {currentDir.findSubdirectory(subPackage)}; 112 | if (subDirectory[0] == null) { 113 | WriteCommandAction.runWriteCommandAction(currentDir.getProject(), () -> { 114 | subDirectory[0] = currentDir.createSubdirectory(subPackage); 115 | log("BaseDirGenerator: " + currentDir.getVirtualFile().getPath() + "/" + subPackage + " generated"); 116 | }); 117 | } 118 | return subDirectory[0]; 119 | } 120 | 121 | /** 122 | * Here the fork appears, sub-class have to implement this method to achieve particular work.
123 | * For example: subPackage = "Acontract"
124 | * Then this method should 125 | *
    126 | *
  1. Move {@link #myContractDir} to {@link #myCurrentDir}.subPackage
  2. 127 | *
  3. Move {@link #myModelDir} to "Amodel"
  4. 128 | *
  5. Move {@link #myPresenterDir} to "Apresenter"
  6. 129 | *
130 | * 131 | * @param subPackage the name of the dir which should have siblings 132 | * @see #moveDirPointer(PsiDirectory, String) 133 | */ 134 | protected abstract void onGenerateForkDirs(@NotNull String subPackage); 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/dir/generator/impl/ContractModeDirGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.dir.generator.impl; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.wingsofts.mvphelper.biz.file.generator.impl.JavaModeFileGenerator; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * @author Administrator 9 | * @since 2017/4/9 10 | */ 11 | public class ContractModeDirGenerator extends BaseDirGenerator { 12 | 13 | public ContractModeDirGenerator(AnActionEvent actionEvent, String prefix) { 14 | super(actionEvent, prefix); 15 | } 16 | 17 | /** 18 | * start generation 19 | */ 20 | @Override 21 | public void start() { 22 | generateDirsBasedOnSuffix("contract"); 23 | 24 | new JavaModeFileGenerator(myProject, myContractDir, myModelDir, myPresenterDir, myPrefix).start(); 25 | } 26 | 27 | @Override 28 | protected void onGenerateForkDirs(@NotNull String subPackage) { 29 | String prefix = subPackage.replace("contract", ""); 30 | String subPackageM = prefix + "model"; 31 | String subPackageP = prefix + "presenter";//if prefix exist. 32 | myContractDir = myCurrentDir.findSubdirectory(subPackage); 33 | myModelDir = moveDirPointer(myCurrentDir, subPackageM); 34 | myPresenterDir = moveDirPointer(myCurrentDir, subPackageP); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/dir/generator/impl/PresenterModeDirGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.dir.generator.impl; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.wingsofts.mvphelper.biz.file.generator.impl.JavaModeFileGenerator; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * @author Administrator 9 | * @since 2017/4/9 10 | */ 11 | public class PresenterModeDirGenerator extends BaseDirGenerator { 12 | 13 | public PresenterModeDirGenerator(AnActionEvent actionEvent, String prefix) { 14 | super(actionEvent, prefix); 15 | } 16 | 17 | /** 18 | * start generation 19 | */ 20 | @Override 21 | public void start() { 22 | generateDirsBasedOnSuffix("presenter"); 23 | 24 | new JavaModeFileGenerator(myProject, myContractDir, myModelDir, myPresenterDir, myPrefix).start(); 25 | } 26 | 27 | @Override 28 | protected void onGenerateForkDirs(@NotNull String subPackage) { 29 | String prefix = subPackage.replace("presenter", ""); 30 | String subPackageM = prefix + "model"; 31 | String subPackageC = prefix + "contract";//if prefix exist. 32 | myContractDir = moveDirPointer(myCurrentDir, subPackageC); 33 | myModelDir = moveDirPointer(myCurrentDir, subPackageM); 34 | myPresenterDir = myCurrentDir.findSubdirectory(subPackage); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/file/generator/FileGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.file.generator; 2 | 3 | import com.wingsofts.mvphelper.biz.Startable; 4 | 5 | /** 6 | * The interface witch provide methods to start files and contents. 7 | * 8 | * @author DengChao 9 | * @since 2017/4/10 10 | */ 11 | public interface FileGenerator extends Startable { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/file/generator/impl/BaseFileGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.file.generator.impl; 2 | 3 | import com.intellij.ide.fileTemplates.JavaTemplateUtil; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.psi.*; 8 | import com.intellij.psi.search.GlobalSearchScope; 9 | import com.intellij.psi.search.PsiShortNamesCache; 10 | import com.wingsofts.mvphelper.biz.config.MvpConfigurable; 11 | import com.wingsofts.mvphelper.biz.config.impl.MvpConfig; 12 | import com.wingsofts.mvphelper.biz.file.generator.FileGenerator; 13 | 14 | import static com.wingsofts.mvphelper.biz.EventLogger.log; 15 | 16 | /** 17 | * The base file generator, provide basic functions for child-class. 18 | * 19 | * @author Administrator 20 | * @since 2017/4/24. 21 | */ 22 | @SuppressWarnings({"ConstantConditions", "WeakerAccess"}) 23 | abstract class BaseFileGenerator implements FileGenerator { 24 | protected Project myProject;//current java project 25 | protected PsiDirectory myContractDir;//the contract package dir 26 | protected PsiDirectory myModelDir;//the model package dir 27 | protected PsiDirectory myPresenterDir;//the presenter package dir 28 | protected String myPrefix;//the prefix used to identify each other 29 | protected PsiElementFactory myFactory;//the factory used to generate interface/class/innerClass/classReference 30 | protected JavaDirectoryService myDirectoryService;//the dirService used to generate files under particular dir(package) 31 | protected PsiShortNamesCache myShortNamesCache;//used to search a class in particular scope 32 | protected GlobalSearchScope myProjectScope;//just this project is enough 33 | private MvpConfigurable myConfig; 34 | 35 | BaseFileGenerator(Project project, PsiDirectory contractDir, PsiDirectory modelDir, PsiDirectory presenterDir, String prefix) { 36 | this.myProject = project; 37 | this.myContractDir = contractDir; 38 | this.myModelDir = modelDir; 39 | this.myPresenterDir = presenterDir; 40 | this.myPrefix = prefix; 41 | myShortNamesCache = PsiShortNamesCache.getInstance(project); 42 | myFactory = JavaPsiFacade.getElementFactory(project); 43 | myDirectoryService = JavaDirectoryService.getInstance(); 44 | myProjectScope = GlobalSearchScope.projectScope(project); 45 | myConfig = new MvpConfig(project); 46 | } 47 | 48 | /** 49 | * Generate a java file 50 | * 51 | * @param directory the directory to place the file 52 | * @param fileName the name of the file tobe generate 53 | * @param type the type of the file 54 | * @param listener when the file has been generated, then the listener will be called. 55 | * @see JavaTemplateUtil#INTERNAL_CLASS_TEMPLATES 56 | */ 57 | protected void generateFile(final PsiDirectory directory, final String fileName, final String type, final OnFileGeneratedListener listener) { 58 | WriteCommandAction.runWriteCommandAction(myProject, () -> { 59 | String fixedFileName = fileName; 60 | if (myConfig.hasSuffix() && type.equals(JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME)) { 61 | //only sub-class need to fix name 62 | fixedFileName += myConfig.getSuffix(); 63 | } 64 | 65 | PsiClass[] psiClasses = myShortNamesCache.getClassesByName(fixedFileName, myProjectScope);//NotNull 66 | PsiClass psiClass; 67 | PsiJavaFile javaFile; 68 | VirtualFile virtualFile = directory.getVirtualFile();// VirtualDirectoryImpl 69 | String virtualFilePath; 70 | for (PsiClass aClass : psiClasses) { 71 | javaFile = (PsiJavaFile) aClass.getContainingFile(); 72 | virtualFilePath = virtualFile.getPath(); 73 | if (virtualFilePath.endsWith(javaFile.getPackageName().replaceAll("\\.", "/"))) {//if the class already exist. 74 | javaFile = (PsiJavaFile) aClass.getContainingFile(); 75 | javaFile.delete();//then delete the old one 76 | log("BaseFileGenerator: " + virtualFilePath + "/" + fixedFileName + " old file deleted"); 77 | } 78 | }//and re-generate one 79 | 80 | psiClass = myDirectoryService.createClass(directory, fixedFileName, type); 81 | javaFile = (PsiJavaFile) psiClass.getContainingFile(); 82 | PsiPackage psiPackage = myDirectoryService.getPackage(directory); 83 | javaFile.setPackageName(psiPackage.getQualifiedName()); 84 | log("BaseFileGenerator: " + psiClass.getQualifiedName() + " generated"); 85 | listener.onJavaFileGenerated(javaFile, psiClass); 86 | }); 87 | } 88 | 89 | 90 | @FunctionalInterface 91 | protected interface OnFileGeneratedListener { 92 | /** 93 | * When the file has been generated, then the listener will be called. 94 | * 95 | * @param javaFile the PsiJavaFile generated just now 96 | * @param psiClass the corresponding PsiClass 97 | */ 98 | void onJavaFileGenerated(PsiJavaFile javaFile, PsiClass psiClass); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/biz/file/generator/impl/JavaModeFileGenerator.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.biz.file.generator.impl; 2 | 3 | import com.intellij.ide.fileTemplates.JavaTemplateUtil; 4 | import com.intellij.openapi.fileEditor.FileEditorManager; 5 | import com.intellij.openapi.fileEditor.OpenFileDescriptor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiClass; 8 | import com.intellij.psi.PsiDirectory; 9 | 10 | /** 11 | * The file generator works in Contract/Presenter Mode. 12 | * 13 | * @author DengChao 14 | * @since 2017/4/10 15 | */ 16 | @SuppressWarnings("ConstantConditions") 17 | public class JavaModeFileGenerator extends BaseFileGenerator { 18 | 19 | public JavaModeFileGenerator(Project project, PsiDirectory contractDir, PsiDirectory modelDir, PsiDirectory presenterDir, String prefix) { 20 | super(project, contractDir, modelDir, presenterDir, prefix); 21 | } 22 | 23 | @Override 24 | public void start() { 25 | generateFile(myContractDir, myPrefix + "Contract", JavaTemplateUtil.INTERNAL_INTERFACE_TEMPLATE_NAME, (javaFile, psiClass) -> { 26 | PsiClass model = myFactory.createInterface("Model"); 27 | PsiClass view = myFactory.createInterface("View");//You have to achieve 'View' yourself. 28 | PsiClass presenter = myFactory.createInterface("Presenter"); 29 | 30 | model.getModifierList().setModifierProperty("public", false);//Remove modifier 31 | view.getModifierList().setModifierProperty("public", false); 32 | presenter.getModifierList().setModifierProperty("public", false); 33 | 34 | psiClass.add(model); 35 | psiClass.add(view); 36 | psiClass.add(presenter); 37 | psiClass.getModifierList().setModifierProperty("public", true);//force 'public interface myPrefixContract' 38 | 39 | FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject); 40 | OpenFileDescriptor fileDescriptor = new OpenFileDescriptor(myProject, javaFile.getVirtualFile()); 41 | fileEditorManager.openTextEditor(fileDescriptor, true);//Open the Contract 42 | 43 | generateModel(); 44 | generatePresenter(); 45 | }); 46 | } 47 | 48 | private void generatePresenter() { 49 | generateFile(myPresenterDir, myPrefix + "Presenter", JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, (javaFile, psiClass) -> { 50 | PsiClass contractClass = myShortNamesCache.getClassesByName(myPrefix + "Contract", myProjectScope)[0]; 51 | PsiClass presenter = contractClass.findInnerClassByName("Presenter", false);//don't need to search base 52 | psiClass.getImplementsList().add(myFactory.createClassReferenceElement(presenter)); 53 | psiClass.getModifierList().setModifierProperty("public", true);//force 'public interface myPrefixContract' 54 | }); 55 | } 56 | 57 | private void generateModel() { 58 | generateFile(myModelDir, myPrefix + "Model", JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, (javaFile, psiClass) -> { 59 | PsiClass contractClass = myShortNamesCache.getClassesByName(myPrefix + "Contract", myProjectScope)[0]; 60 | PsiClass model = contractClass.findInnerClassByName("Model", false);//don't need to search base 61 | psiClass.getImplementsList().add(myFactory.createClassReferenceElement(model)); 62 | psiClass.getModifierList().setModifierProperty("public", true);//force 'public interface myPrefixContract' 63 | }); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/gui/MvpHelperConfigPanel.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
91 | -------------------------------------------------------------------------------- /src/com/wingsofts/mvphelper/gui/MvpHelperConfigPanel.java: -------------------------------------------------------------------------------- 1 | package com.wingsofts.mvphelper.gui; 2 | 3 | import com.intellij.ide.util.PropertiesComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.options.Configurable; 7 | import com.intellij.openapi.options.UnnamedConfigurable; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.ui.Messages; 10 | import com.wingsofts.mvphelper.biz.config.MvpConfigurable; 11 | import org.jetbrains.annotations.Nls; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import javax.swing.*; 15 | import javax.swing.event.CaretEvent; 16 | import java.awt.*; 17 | import java.awt.event.MouseAdapter; 18 | import java.awt.event.MouseEvent; 19 | import java.net.URI; 20 | import java.util.ResourceBundle; 21 | 22 | /** 23 | * @author DengChao 24 | * @since 2017/4/24 25 | */ 26 | @State(name = "MvpHelperConfig", storages = {@Storage("MvpHelperConfig.xml")})//try register a storage place 27 | public class MvpHelperConfigPanel implements Configurable, MvpConfigurable { 28 | private static final String PREFIX = "MVP_HELPER_"; 29 | private static final String KEY_HAS_SUFFIX = PREFIX + "KEY_HAS_SUFFIX"; 30 | private static final String KEY_SUFFIX = PREFIX + "KEY_SUFFIX"; 31 | private ResourceBundle string;//the static resource bundle 32 | private PropertiesComponent properties;//the properties object, used to save and load property. 33 | private String helloWorldModel; 34 | private String suffix; 35 | private JCheckBox ckEnableSuffix; 36 | private JTextField tfSuffixTemplate; 37 | private JTextArea taSuffixExample; 38 | private JLabel lFeedBack; 39 | private JLabel lOpenOnGitHub; 40 | private JPanel panel; 41 | private boolean hasSuffix; 42 | private boolean hasSuffixNewValue; 43 | 44 | /** 45 | * Constructor for Intellij Platform usage 46 | */ 47 | @SuppressWarnings("unused") 48 | public MvpHelperConfigPanel(Project project) { 49 | this(project, true); 50 | } 51 | 52 | /** 53 | * Constructor for User usage 54 | * 55 | * @param project the project witch need to save and load config 56 | * @param fullInit is all Swing object need to init listeners 57 | */ 58 | public MvpHelperConfigPanel(Project project, boolean fullInit) { 59 | string = ResourceBundle.getBundle("string"); 60 | properties = PropertiesComponent.getInstance(project);//always project level. 61 | helloWorldModel = string.getString("suffixExample"); 62 | 63 | hasSuffixNewValue = hasSuffix = properties.getBoolean(KEY_HAS_SUFFIX);//they are the same when init 64 | suffix = hasSuffix ? properties.getValue(KEY_SUFFIX) : "";//init update 65 | 66 | if (!fullInit) { 67 | return; 68 | } 69 | 70 | ckEnableSuffix.addItemListener(event -> { 71 | hasSuffixNewValue = ckEnableSuffix.isSelected();//then user changed the new value 72 | tfSuffixTemplate.setFocusable(hasSuffixNewValue); 73 | tfSuffixTemplate.setEnabled(hasSuffixNewValue); 74 | 75 | //no matter enable or disable, just reset the suffix template 76 | tfSuffixTemplate.setText(""); 77 | taSuffixExample.setText(helloWorldModel); 78 | }); 79 | 80 | tfSuffixTemplate.addCaretListener((CaretEvent caretEvent) -> 81 | taSuffixExample.setText(helloWorldModel + tfSuffixTemplate.getText()));//dynamic update 82 | 83 | ckEnableSuffix.setSelected(hasSuffix); 84 | tfSuffixTemplate.setEnabled(hasSuffix); 85 | tfSuffixTemplate.setFocusable(hasSuffix); 86 | taSuffixExample.setText(helloWorldModel + suffix); 87 | 88 | lFeedBack.addMouseListener(new MouseAdapter() { 89 | @Override 90 | public void mouseClicked(MouseEvent e) { 91 | String feedBackUrl = string.getString("feedBackUrl"); 92 | try { 93 | Desktop.getDesktop().browse(URI.create(feedBackUrl)); 94 | } catch (Exception exception) { 95 | Messages.showErrorDialog( 96 | "Oh..It seems we cannot open any browser on this platform automatically." + 97 | " But you could do it yourself:\n" + 98 | feedBackUrl, "Browse Error"); 99 | exception.printStackTrace(); 100 | } 101 | } 102 | 103 | }); 104 | 105 | lOpenOnGitHub.addMouseListener(new MouseAdapter() { 106 | @Override 107 | public void mouseClicked(MouseEvent e) { 108 | String gitHubUrl = string.getString("gitHubUrl"); 109 | try { 110 | Desktop.getDesktop().browse(URI.create(gitHubUrl)); 111 | } catch (Exception exception) { 112 | Messages.showErrorDialog( 113 | "Oh..It seems we cannot open any browser on this platform automatically." + 114 | " But you could do it yourself:\n" + 115 | gitHubUrl, "Browse Error"); 116 | exception.printStackTrace(); 117 | } 118 | } 119 | }); 120 | } 121 | 122 | @Nls 123 | @Override 124 | public String getDisplayName() { 125 | return "Mvp Helper"; 126 | } 127 | 128 | @Nullable 129 | @Override 130 | public JComponent createComponent() { 131 | return panel; 132 | } 133 | 134 | @Override 135 | public boolean isModified() { 136 | return hasSuffix != hasSuffixNewValue || !suffix.equals(tfSuffixTemplate.getText()); 137 | } 138 | 139 | @Override 140 | public void reset() { 141 | ckEnableSuffix.setSelected(hasSuffix); 142 | tfSuffixTemplate.setText(suffix); 143 | taSuffixExample.setText(helloWorldModel + suffix); 144 | 145 | properties.setValue(KEY_HAS_SUFFIX, hasSuffix); 146 | properties.setValue(KEY_SUFFIX, suffix); 147 | } 148 | 149 | @Override 150 | public void apply() { 151 | String suffixNewValue = tfSuffixTemplate.getText(); 152 | properties.setValue(KEY_HAS_SUFFIX, hasSuffixNewValue); 153 | properties.setValue(KEY_SUFFIX, suffixNewValue); 154 | 155 | hasSuffix = hasSuffixNewValue;//update isModified() 156 | suffix = suffixNewValue; 157 | } 158 | 159 | /** 160 | * Override this to avoid AbstractMethodException, 161 | * for the IDEA plugin dev-platform use Java 8's feature: default interface 162 | * 163 | * @see UnnamedConfigurable#disposeUIResources() 164 | */ 165 | @Override 166 | public void disposeUIResources() { 167 | //noinspection BoundFieldAssignment 168 | panel = null; 169 | } 170 | 171 | /** 172 | * Only available in MvpHelperConfigPanel(project,false) 173 | */ 174 | @Override 175 | public String getSuffix() { 176 | return suffix; 177 | } 178 | 179 | /** 180 | * Only available in MvpHelperConfigPanel(project,false) 181 | */ 182 | @Override 183 | public boolean hasSuffix() { 184 | return hasSuffix; 185 | } 186 | 187 | } 188 | --------------------------------------------------------------------------------