package com.google.jstestdriver.idea.assertFramework.support; import com.google.common.collect.Lists; import com.google.jstestdriver.idea.util.ProjectRootUtils; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.ui.Gray; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.components.JBList; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.SwingHelper; import com.intellij.webcore.ScriptingFrameworkDescriptor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.List; public class AddAdapterSupportDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance(AddAdapterSupportDialog.class); private final Project myProject; private final String myAssertFrameworkName; private final List<VirtualFile> myAdapterSourceFiles; private final VirtualFile myFileRequestor; private final JPanel myContent; private final JTextField myDirectoryTextField; public AddAdapterSupportDialog(@NotNull Project project, @NotNull PsiFile psiFileRequestor, @NotNull String assertionFrameworkName, @NotNull List<VirtualFile> adapterSourceFiles, @Nullable String adapterHomePageUrl) { super(project); myProject = project; myAssertFrameworkName = assertionFrameworkName; myAdapterSourceFiles = adapterSourceFiles; myFileRequestor = psiFileRequestor.getVirtualFile(); setModal(true); setTitle("Add " + getAssertFrameworkAdapterName()); myDirectoryTextField = new JTextField(); VirtualFile initialDir = findInitialDir(psiFileRequestor); if (initialDir != null) { myDirectoryTextField.setText(FileUtil.toSystemDependentName(initialDir.getPath())); } // widen preferred size to fit dialog's title myDirectoryTextField.setPreferredSize(new Dimension(350, myDirectoryTextField.getPreferredSize().height)); List<Component> components = Lists.newArrayList(); components.add(createFilesViewPanel(adapterSourceFiles)); components.add(Box.createVerticalStrut(10)); components.add(createSelectDirectoryPanel(project, myDirectoryTextField)); if (adapterHomePageUrl != null) { components.add(Box.createVerticalStrut(10)); components.add(createInformationPanel(adapterHomePageUrl)); } myContent = SwingHelper.newLeftAlignedVerticalPanel(components); setOKButtonText("Add"); super.init(); } @NotNull private static JComponent createInformationPanel(@NotNull final String adapterHomePageUrl) { JLabel label1 = new JLabel("See"); HyperlinkLabel hyperlink = SwingHelper.createWebHyperlink(adapterHomePageUrl); JLabel label2 = new JLabel("for details."); JPanel panel = SwingHelper.newHorizontalPanel( Component.BOTTOM_ALIGNMENT, SwingHelper.newLeftAlignedVerticalPanel(label1, Box.createVerticalStrut(2)), hyperlink, Box.createHorizontalStrut(5), SwingHelper.newLeftAlignedVerticalPanel(label2, Box.createVerticalStrut(2)) ); return SwingHelper.wrapWithoutStretch(panel); } @Nullable private static VirtualFile findInitialDir(@NotNull final PsiFile psiFileRequestor) { if (!psiFileRequestor.isValid()) { return null; } return ReadAction.compute(() -> { Project project = psiFileRequestor.getProject(); VirtualFile virtualFile = psiFileRequestor.getVirtualFile(); if (virtualFile != null) { ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); VirtualFile contentRoot = fileIndex.getContentRootForFile(virtualFile); if (contentRoot != null) { return contentRoot; } } return project.getBaseDir(); }); } @NotNull private static JPanel createFilesViewPanel(@NotNull List<VirtualFile> files) { JPanel panel = new JPanel(new BorderLayout(0, 2)); panel.add(new JLabel("Files to add:"), BorderLayout.NORTH); final JBList fileList = new JBList(ArrayUtil.EMPTY_STRING_ARRAY); fileList.setBorder(BorderFactory.createLineBorder(Color.lightGray)); fileList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { fileList.clearSelection(); } }); fileList.setFocusable(false); fileList.setRequestFocusEnabled(false); fileList.setBackground(Gray._242); fileList.setCellRenderer(new ListCellRendererWrapper<VirtualFile>() { @Override public void customize(JList list, VirtualFile value, int index, boolean selected, boolean hasFocus) { setText(" " + value.getName()); } }); fileList.setListData(files.toArray()); panel.add(fileList, BorderLayout.CENTER); return panel; } @NotNull private JPanel createSelectDirectoryPanel(@NotNull Project project, @NotNull JTextField directoryTextField) { FileChooserDescriptor fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); String adapterName = getAssertFrameworkAdapterName(); String title = "Select a directory for " + adapterName + " files"; String description = adapterName + " source files will be copied to the selected directory"; TextFieldWithBrowseButton directoryTextFieldWithBrowseButton = new TextFieldWithBrowseButton(directoryTextField); directoryTextFieldWithBrowseButton.addBrowseFolderListener( title, description, project, fileChooserDescriptor ); Dimension oldDimension = directoryTextFieldWithBrowseButton.getPreferredSize(); directoryTextFieldWithBrowseButton.setMaximumSize(oldDimension); JPanel panel = new JPanel(new BorderLayout(0, 2)); panel.add(new JLabel("Copy these files to directory:"), BorderLayout.NORTH); panel.add(directoryTextFieldWithBrowseButton, BorderLayout.CENTER); return SwingHelper.wrapWithHorizontalStretch(panel); } private String getAssertFrameworkAdapterName() { return myAssertFrameworkName + " JsTestDriver Adapter"; } @Override protected JComponent createCenterPanel() { return myContent; } @Override public JComponent getPreferredFocusedComponent() { return myDirectoryTextField; } @Override @Nullable protected ValidationInfo doValidate() { String text = myDirectoryTextField.getText(); File dir = new File(text); if (!dir.isDirectory() || !dir.isAbsolute()) { return new ValidationInfo("Not a valid directory", myDirectoryTextField); } return null; } @Override protected void doOKAction() { List<VirtualFile> extractedVirtualFiles = extractAdapterFiles(); if (extractedVirtualFiles != null) { installCodeAssistance(extractedVirtualFiles); } super.doOKAction(); } /** * @return extracted file list or null if extraction failed */ @Nullable private List<VirtualFile> extractAdapterFiles() { return WriteAction.compute(() -> { try { VirtualFile extractDir = getOrCreateExtractDirVirtualFile(); return copyVirtualFilesToDir(extractDir); } catch (Exception e) { LOG.warn("Extraction of " + getAssertFrameworkAdapterName() + " files failed", e); return null; } }); } @NotNull private VirtualFile getOrCreateExtractDirVirtualFile() { File extractDir = new File(myDirectoryTextField.getText()); VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(extractDir); if (vFile == null || !vFile.isValid()) { vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(extractDir); if (vFile == null || !vFile.isValid()) { throw new RuntimeException("Can't find valid VirtualFile for " + extractDir.getAbsolutePath()); } } return vFile; } @Nullable private List<VirtualFile> copyVirtualFilesToDir(@NotNull VirtualFile targetDir) { List<VirtualFile> copiedFiles = Lists.newArrayList(); for (VirtualFile virtualFile : myAdapterSourceFiles) { try { //noinspection NullableProblems copiedFiles.add(VfsUtilCore.copyFile(null, virtualFile, targetDir)); } catch (IOException e) { Messages.showErrorDialog("Extract operation failed!\nUnable to copy " + virtualFile.getPath() + " to " + targetDir.getPath(), "Adding " + getAssertFrameworkAdapterName()); return null; } } return copiedFiles; } public void installCodeAssistance(final List<VirtualFile> extractedAdapterSourceFiles) { boolean createLibrary = ReadAction.compute(() -> { File extractDir = new File(myDirectoryTextField.getText()); return !ProjectRootUtils.isInsideContentRoots(myProject, extractDir); }); if (createLibrary) { ChooseScopeAndCreateLibraryDialog dialog = new ChooseScopeAndCreateLibraryDialog( myProject, getAssertFrameworkAdapterName(), extractedAdapterSourceFiles, new ScriptingFrameworkDescriptor(getAssertFrameworkAdapterName(), null), myFileRequestor, true ); dialog.show(); } } }