Java tutorial
/* * Copyright 2016 The Bazel Authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.idea.blaze.base.wizard; import com.google.common.collect.Lists; import com.google.idea.blaze.base.bazel.BuildSystemProvider; import com.google.idea.blaze.base.model.primitives.TargetExpression; import com.google.idea.blaze.base.model.primitives.WorkspacePath; import com.google.idea.blaze.base.model.primitives.WorkspaceRoot; import com.google.idea.blaze.base.projectview.ProjectView; import com.google.idea.blaze.base.projectview.ProjectViewSet; import com.google.idea.blaze.base.projectview.ProjectViewStorageManager; import com.google.idea.blaze.base.projectview.parser.ProjectViewParser; import com.google.idea.blaze.base.projectview.section.ListSection; import com.google.idea.blaze.base.projectview.section.ScalarSection; import com.google.idea.blaze.base.projectview.section.sections.DirectoryEntry; import com.google.idea.blaze.base.projectview.section.sections.DirectorySection; import com.google.idea.blaze.base.projectview.section.sections.ImportSection; import com.google.idea.blaze.base.projectview.section.sections.TargetSection; import com.google.idea.blaze.base.scope.output.IssueOutput; import com.google.idea.blaze.base.settings.Blaze.BuildSystem; import com.google.idea.blaze.base.settings.BlazeImportSettings; import com.google.idea.blaze.base.settings.ui.JPanelProvidingProject; import com.google.idea.blaze.base.settings.ui.ProjectViewUi; import com.google.idea.blaze.base.ui.BlazeValidationError; import com.google.idea.blaze.base.ui.BlazeValidationResult; import com.google.idea.blaze.base.ui.UiUtil; import com.google.idea.blaze.base.vcs.BlazeVcsHelper; import com.intellij.ide.RecentProjectsManager; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.ui.TextComponentAccessor; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.components.JBLabel; import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; /** * The UI control to collect project settings when importing a Blaze project. */ public final class BlazeProjectSettingsControl { private static final FileChooserDescriptor STUDIO_PROJECT_FOLDER_DESCRIPTOR = new FileChooserDescriptor(false, true, false, false, false, false); private static final Logger LOG = Logger.getInstance(BlazeProjectSettingsControl.class); private WorkspaceRoot workspaceRoot; @Nullable private File sharedProjectViewFile; @Nullable private String vcsClientName; private TextFieldWithBrowseButton projectDataDirField; private JTextField projectNameField; private ProjectViewUi projectViewUi; public BlazeProjectSettingsControl(Disposable parentDisposable) { this.projectViewUi = new ProjectViewUi(parentDisposable); } public JPanel createComponent(File fileToImport) { JPanel component = new JPanelProvidingProject(ProjectViewUi.getProject(), new GridBagLayout()); fillUi(component, 0); init(fileToImport); UiUtil.fillBottom(component); return component; } private void fillUi(@NotNull JPanel canvas, int indentLevel) { JLabel projectDataDirLabel = new JBLabel("Project data directory:"); Dimension minSize = ProjectViewUi.getMinimumSize(); // Add 120 pixels so we have room for our extra fields minSize.setSize(minSize.width, minSize.height + 120); canvas.setMinimumSize(minSize); canvas.setPreferredSize(minSize); projectDataDirField = new TextFieldWithBrowseButton(); projectDataDirField.addBrowseFolderListener("", "Blaze Android Studio project data directory", null, STUDIO_PROJECT_FOLDER_DESCRIPTOR, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT, false); final String dataDirToolTipText = "Directory in which to store the project's metadata. Choose a directory outside of" + " the Piper/CitC client directories or Git5 directory."; projectDataDirField.setToolTipText(dataDirToolTipText); projectDataDirLabel.setToolTipText(dataDirToolTipText); canvas.add(projectDataDirLabel, UiUtil.getLabelConstraints(indentLevel)); canvas.add(projectDataDirField, UiUtil.getFillLineConstraints(0)); JLabel projectNameLabel = new JLabel("Project name:"); projectNameField = new JTextField(); final String projectNameToolTipText = "Project display name."; projectNameField.setToolTipText(projectNameToolTipText); projectNameLabel.setToolTipText(projectNameToolTipText); canvas.add(projectNameLabel, UiUtil.getLabelConstraints(indentLevel)); canvas.add(projectNameField, UiUtil.getFillLineConstraints(0)); projectViewUi.fillUi(canvas, indentLevel); } private void init(File fileToImport) { workspaceRoot = BuildSystemProvider.getWorkspaceRootProvider(BuildSystem.Blaze) .findWorkspaceRoot(fileToImport); if (workspaceRoot == null) { throw new IllegalArgumentException("Invalid workspace root: " + fileToImport); } vcsClientName = BlazeVcsHelper.getClientName(workspaceRoot); File importDirectory = null; if (ProjectViewStorageManager.isProjectViewFile(fileToImport.getPath())) { importDirectory = fileToImport.getParentFile(); sharedProjectViewFile = new File(fileToImport.getPath()); } else if (ImportSource.isBuildFile(fileToImport)) { importDirectory = fileToImport.getParentFile(); for (String extension : ProjectViewStorageManager.VALID_EXTENSIONS) { File defaultProjectViewFile = new File(fileToImport.getParentFile(), "." + extension); if (defaultProjectViewFile.exists()) { sharedProjectViewFile = defaultProjectViewFile; break; } } } String defaultProjectName = importDirectory != null ? importDirectory.getName() : workspaceRoot.directory().getParentFile().getName(); projectNameField.setText(defaultProjectName); String defaultDataDir = getDefaultProjectDataDirectory(defaultProjectName, vcsClientName); projectDataDirField.setText(defaultDataDir); String projectViewText = ""; if (sharedProjectViewFile != null) { try { projectViewText = ProjectViewStorageManager.getInstance().loadProjectView(sharedProjectViewFile); if (projectViewText == null) { LOG.error("Could not load project view: " + sharedProjectViewFile); projectViewText = ""; } } catch (IOException e) { LOG.error(e); } } if (projectViewText.isEmpty() && importDirectory != null) { projectViewText = guessProjectViewFromLocation(workspaceRoot, importDirectory); } projectViewUi.init(workspaceRoot, projectViewText, sharedProjectViewFile != null ? projectViewText : null, sharedProjectViewFile, sharedProjectViewFile != null, false /* allowEditShared - not allowed during import */ ); } @NotNull private static String getDefaultProjectDataDirectory(@NotNull String projectName, @Nullable String vcsClientName) { File defaultDataDirectory = new File(getDefaultProjectsDirectory()); if (vcsClientName != null) { // Ensure that each client gets its own data directory. projectName = vcsClientName + "-" + projectName; } File desiredLocation = new File(defaultDataDirectory, projectName); return newUniquePath(desiredLocation); } @NotNull private static String getDefaultProjectsDirectory() { final String lastProjectLocation = RecentProjectsManager.getInstance().getLastProjectCreationLocation(); if (lastProjectLocation != null) { return lastProjectLocation.replace('/', File.separatorChar); } final String userHome = SystemProperties.getUserHome(); String productName = ApplicationNamesInfo.getInstance().getLowercaseProductName(); return userHome.replace('/', File.separatorChar) + File.separator + productName.replace(" ", "") + "Projects"; } @NotNull private static String guessProjectViewFromLocation(@NotNull WorkspaceRoot workspaceRoot, @NotNull File importDirectory) { WorkspacePath mainModuleGoogle3RelativePath = workspaceRoot.workspacePathFor(importDirectory); WorkspacePath testModuleGoogle3RelativePath = guessTestRelativePath(workspaceRoot, mainModuleGoogle3RelativePath); ListSection.Builder<DirectoryEntry> directorySectionBuilder = ListSection.builder(DirectorySection.KEY); directorySectionBuilder.add(DirectoryEntry.include(mainModuleGoogle3RelativePath)); if (testModuleGoogle3RelativePath != null) { directorySectionBuilder.add(DirectoryEntry.include(testModuleGoogle3RelativePath)); } ListSection.Builder<TargetExpression> targetSectionBuilder = ListSection.builder(TargetSection.KEY); targetSectionBuilder.add(TargetExpression.fromString("//" + mainModuleGoogle3RelativePath + "/...:all")); if (testModuleGoogle3RelativePath != null) { targetSectionBuilder .add(TargetExpression.fromString("//" + testModuleGoogle3RelativePath + "/...:all")); } return ProjectViewParser.projectViewToString( ProjectView.builder().put(directorySectionBuilder).put(targetSectionBuilder).build()); } @Nullable private static WorkspacePath guessTestRelativePath(@NotNull WorkspaceRoot workspaceRoot, @NotNull WorkspacePath projectWorkspacePath) { String projectRelativePath = projectWorkspacePath.relativePath(); String testBuildFileRelativePath = null; if (projectRelativePath.startsWith("java/")) { testBuildFileRelativePath = projectRelativePath.replaceFirst("java/", "javatests/"); } else if (projectRelativePath.contains("/java/")) { testBuildFileRelativePath = projectRelativePath.replaceFirst("/java/", "/javatests/"); } if (testBuildFileRelativePath != null) { File testBuildFile = workspaceRoot.fileForPath(new WorkspacePath(testBuildFileRelativePath)); if (testBuildFile.exists()) { return new WorkspacePath(testBuildFileRelativePath); } } return null; } /** * Returns a unique file path by appending numbers until a non-collision is found. */ private static String newUniquePath(File location) { if (!location.exists()) { return location.getAbsolutePath(); } String name = location.getName(); File directory = location.getParentFile(); int tries = 0; while (true) { String candidateName = String.format("%s-%02d", name, tries); File candidateFile = new File(directory, candidateName); if (!candidateFile.exists()) { return candidateFile.getAbsolutePath(); } tries++; } } @Nullable private BlazeValidationError validateProjectDataDirectory(@NotNull File projectDataDirPath) { if (workspaceRoot.isInWorkspace(projectDataDirPath)) { return new BlazeValidationError( "Project data directory should be placed outside of the client directory"); } return null; } @NotNull public BlazeValidationResult validate() { // Validate project settings fields String projectName = projectNameField.getText().trim(); if (StringUtil.isEmpty(projectName)) { return BlazeValidationResult.failure(new BlazeValidationError("Project name is not specified")); } String projectDataDirPath = projectDataDirField.getText().trim(); if (StringUtil.isEmpty(projectDataDirPath)) { return BlazeValidationResult .failure(new BlazeValidationError("Project data directory is not specified")); } File projectDataDir = new File(projectDataDirPath); if (!projectDataDir.isAbsolute()) { return BlazeValidationResult.failure(new BlazeValidationError("Project data directory is not valid")); } BlazeValidationError projectDataDirectoryValidation = validateProjectDataDirectory(projectDataDir); if (projectDataDirectoryValidation != null) { return BlazeValidationResult.failure(projectDataDirectoryValidation); } List<IssueOutput> issues = Lists.newArrayList(); ProjectViewSet projectViewSet = projectViewUi.parseProjectView(issues); BlazeValidationError projectViewParseError = validationErrorFromIssueList(issues); if (projectViewParseError != null) { return BlazeValidationResult.failure(projectViewParseError); } return BlazeValidationResult.success(); } public ImportResults getResults() { String projectName = projectNameField.getText().trim(); String projectDataDirectory = projectDataDirField.getText().trim(); // Create unique location hash final String locationHash = createLocationHash(projectName); // Only support blaze in the old import wizard. TODO: remove this wizard prior to public bazel release. BuildSystem fixedBuildSystem = BuildSystem.Blaze; File sharedProjectViewFile = this.sharedProjectViewFile; File localProjectViewFile = ProjectViewStorageManager.getLocalProjectViewFileName(fixedBuildSystem, new File(projectDataDirectory)); BlazeImportSettings importSettings = new BlazeImportSettings(workspaceRoot.directory().getPath(), projectName, projectDataDirectory, locationHash, localProjectViewFile.getPath(), fixedBuildSystem); boolean useSharedProjectView = projectViewUi.getUseSharedProjectView(); // If we're using a shared project view, synthesize a local one that imports the shared one final ProjectView projectView; if (useSharedProjectView && sharedProjectViewFile != null) { WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings); projectView = ProjectView.builder().put(ScalarSection.builder(ImportSection.KEY) .set(workspaceRoot.workspacePathFor(sharedProjectViewFile))).build(); } else { ProjectViewSet parseResult = projectViewUi.parseProjectView(Lists.<IssueOutput>newArrayList()); ProjectViewSet.ProjectViewFile projectViewFile = parseResult.getTopLevelProjectViewFile(); assert projectViewFile != null; projectView = projectViewFile.projectView; } return new ImportResults(importSettings, projectView, calculateProjectName(projectName, vcsClientName), projectDataDirectory); } @NotNull private static String createLocationHash(@NotNull String projectName) { String uuid = UUID.randomUUID().toString(); uuid = uuid.substring(0, Math.min(uuid.length(), 8)); return projectName.replaceAll("[^a-zA-Z0-9]", "") + "-" + uuid; } private static String calculateProjectName(@NotNull String projectName, @Nullable String vcsClientName) { if (vcsClientName != null) { projectName = String.format("%s (%s)", projectName, vcsClientName); } return projectName; } @Nullable private static BlazeValidationError validationErrorFromIssueList(List<IssueOutput> issues) { List<IssueOutput> errors = issues.stream() .filter(issue -> issue.getCategory() == IssueOutput.Category.ERROR).collect(Collectors.toList()); if (!errors.isEmpty()) { StringBuilder errorMessage = new StringBuilder(); errorMessage.append("The following issues were found:\n\n"); for (IssueOutput issue : errors) { errorMessage.append(issue.getMessage()); errorMessage.append('\n'); } return new BlazeValidationError(errorMessage.toString()); } return null; } }