package com.wingsofts.mvphelper.biz.dir.generator.impl; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiJavaFile; import com.wingsofts.mvphelper.biz.dir.generator.DirGenerator; import org.jetbrains.annotations.NotNull; import java.util.ResourceBundle; import static com.wingsofts.mvphelper.biz.EventLogger.log; /** * @author DengChao * @see #moveDirPointer(PsiDirectory, String) * @see #generateDirsBasedOnSuffix(String) * @see #onGenerateForkDirs(String) * @since 2017/4/24. */ @SuppressWarnings({"ConstantConditions", "WeakerAccess"}) abstract class BaseDirGenerator implements DirGenerator { protected final Project myProject; protected final String myPackageName;//where the action happens protected PsiDirectory myCurrentDir;//the base line of dir generation protected PsiDirectory myContractDir; protected PsiDirectory myModelDir; protected PsiDirectory myPresenterDir; protected String myPrefix; BaseDirGenerator(@NotNull AnActionEvent actionEvent, @NotNull String prefix) { this.myPrefix = prefix; myProject = actionEvent.getData(PlatformDataKeys.EDITOR).getProject(); PsiJavaFile javaFile = (PsiJavaFile) actionEvent.getData(CommonDataKeys.PSI_FILE); myPackageName = javaFile.getPackageName(); locateRootDir(javaFile.getContainingFile().getParent()); } /** * Locate the root dir of current project.<br/> * For Android studio project, 'java' folder is always the root of any model by default.<br/> * For IDEA java project, 'src' folder is always the root of any model by default. * * @param currentDir where the action happens */ private void locateRootDir(PsiDirectory currentDir) { String currentDirName = currentDir.getName(); if (currentDirName.equals("java") || currentDirName.equals("src")) { myCurrentDir = currentDir; } else { PsiDirectory parent = currentDir.getParent(); if (parent != null) { locateRootDir(parent);//if this folder is not the root, then try its parent. } else { //when there is no more parent, we reached the ROOT of a hard-disk... //if we still can't locate myCurrentDir by now... //I guess..not the plugin's fault.. =( Messages.showErrorDialog( "I can't imagine what happens to your project," + " technically, no project could reach here.\n" + " For your project match the IDEA's 'Java Project' definition," + " and it match our basic rule: 'Contract' under contract-package and 'Presenter' under presenter-package." + " Since it does happened, report us the detail please:" + " image of this dialog, image of your project structure tree, and your description\n" + ResourceBundle.getBundle("string").getString("feedBackUrl"), "Locate Root Dir Error"); throw new RuntimeException("The plugin cannot find a root dir like \"java\" or \"src\""); } } } /** * <i><b>Make sure to call this on sub-class#{@link #start()}'s first line</b></i> * * @param suffix the suffix of the package witch used to determine where should have a fork. */ protected void generateDirsBasedOnSuffix(@NotNull String suffix) { boolean isForkDirGenerated = false; String[] subPackages = myPackageName.split("\\."); for (String subPackage : subPackages) { if (!subPackage.endsWith(suffix)) {//if the package does not end with 'contract': if (isForkDirGenerated) {// and the 'presenter' and 'model' dir already generated, myContractDir = moveDirPointer(myContractDir, subPackage); myModelDir = moveDirPointer(myModelDir, subPackage); myPresenterDir = moveDirPointer(myPresenterDir, subPackage); }// but the 'presenter' and 'model' dir has not been generated, } else { onGenerateForkDirs(subPackage); isForkDirGenerated = true;//update the flag } //the current dir is the base line, so //no matter what happens, move myCurrentDir pointer to it's child; myCurrentDir = moveDirPointer(myCurrentDir, subPackage); } } /** * Move the pointer to it's sub-package.<br/> * sub-package will be generate if it's not exist. * * @param subPackage the sub-package's name */ @NotNull protected PsiDirectory moveDirPointer(@NotNull final PsiDirectory currentDir, @NotNull final String subPackage) { final PsiDirectory[] subDirectory = {currentDir.findSubdirectory(subPackage)}; if (subDirectory[0] == null) { WriteCommandAction.runWriteCommandAction(currentDir.getProject(), () -> { subDirectory[0] = currentDir.createSubdirectory(subPackage); log("BaseDirGenerator: " + currentDir.getVirtualFile().getPath() + "/" + subPackage + " generated"); }); } return subDirectory[0]; } /** * Here the fork appears, sub-class have to implement this method to achieve particular work.<br/> * For example: subPackage = "Acontract"<br/> * Then this method should * <ol> * <li>Move {@link #myContractDir} to {@link #myCurrentDir}.subPackage</li> * <li>Move {@link #myModelDir} to "Amodel"</li> * <li>Move {@link #myPresenterDir} to "Apresenter"</li> * </ol> * * @param subPackage the name of the dir which should have siblings * @see #moveDirPointer(PsiDirectory, String) */ protected abstract void onGenerateForkDirs(@NotNull String subPackage); }