package com.jetbrains.lang.dart.sdk;
import com.intellij.ide.browsers.chrome.ChromeSettings;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComponentWithBrowseButton;
import com.intellij.openapi.ui.TextComponentAccessor;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ArrayUtil;
import com.intellij.util.BooleanFunction;
import com.intellij.util.SmartList;
import com.jetbrains.lang.dart.DartBundle;
import com.jetbrains.lang.dart.ide.runner.client.DartiumUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.JTextComponent;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class DartSdkUtil {
private static final Map<Pair<File, Long>, String> ourVersions = new HashMap<>();
private static final String DART_SDK_KNOWN_PATHS = "DART_SDK_KNOWN_PATHS";
private static final String DARTIUM_KNOWN_PATHS = "DARTIUM_KNOWN_PATHS";
@Nullable
public static String getSdkVersion(final @NotNull String sdkHomePath) {
final File versionFile = new File(sdkHomePath + "/version");
if (versionFile.isFile()) {
final String cachedVersion = ourVersions.get(Pair.create(versionFile, versionFile.lastModified()));
if (cachedVersion != null) return cachedVersion;
}
final String version = readVersionFile(sdkHomePath);
if (version != null) {
ourVersions.put(Pair.create(versionFile, versionFile.lastModified()), version);
return version;
}
return null;
}
private static String readVersionFile(final String sdkHomePath) {
final File versionFile = new File(sdkHomePath + "/version");
if (versionFile.isFile() && versionFile.length() < 100) {
try {
return FileUtil.loadFile(versionFile).trim();
}
catch (IOException e) {
/* ignore */
}
}
return null;
}
@Contract("null->false")
public static boolean isDartSdkHome(@Nullable final String path) {
return path != null && !path.isEmpty() && new File(path + "/lib/core/core.dart").isFile();
}
public static void initDartSdkAndDartiumControls(final @Nullable Project project,
final @NotNull ComboboxWithBrowseButton dartSdkPathComponent,
final @NotNull JBLabel versionLabel,
final @NotNull ComboboxWithBrowseButton dartiumPathComponent,
final @NotNull Computable<ChromeSettings> currentDartiumSettingsRetriever,
final @NotNull JButton dartiumSettingsButton,
final @NotNull Computable<Boolean> isResettingControlsComputable) {
dartSdkPathComponent.getComboBox().setEditable(true);
addKnownPathsToCombo(dartSdkPathComponent.getComboBox(), DART_SDK_KNOWN_PATHS, DartSdkUtil::isDartSdkHome);
dartiumPathComponent.getComboBox().setEditable(true);
addKnownPathsToCombo(dartiumPathComponent.getComboBox(), DARTIUM_KNOWN_PATHS, path -> !path.isEmpty() && new File(path).exists());
final String sdkHomePath = dartSdkPathComponent.getComboBox().getEditor().getItem().toString().trim();
versionLabel.setText(sdkHomePath.isEmpty() ? "" : getSdkVersion(sdkHomePath));
final TextComponentAccessor<JComboBox> textComponentAccessor = new TextComponentAccessor<JComboBox>() {
@Override
public String getText(final JComboBox component) {
return component.getEditor().getItem().toString();
}
@Override
public void setText(@NotNull final JComboBox component, @NotNull final String text) {
if (!text.isEmpty() && !isDartSdkHome(text)) {
final String probablySdkPath = text + "/dart-sdk";
if (isDartSdkHome(probablySdkPath)) {
component.getEditor().setItem(FileUtilRt.toSystemDependentName(probablySdkPath));
return;
}
}
component.getEditor().setItem(FileUtilRt.toSystemDependentName(text));
}
};
final ComponentWithBrowseButton.BrowseFolderActionListener<JComboBox> browseFolderListener =
new ComponentWithBrowseButton.BrowseFolderActionListener<>("Select Dart SDK path", null, dartSdkPathComponent, project,
FileChooserDescriptorFactory.createSingleFolderDescriptor(),
textComponentAccessor);
dartSdkPathComponent.addActionListener(browseFolderListener);
dartiumPathComponent.addBrowseFolderListener("Select Dartium browser path", null, project,
FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor(),
TextComponentAccessor.STRING_COMBOBOX_WHOLE_TEXT);
final JTextComponent editorComponent = (JTextComponent)dartSdkPathComponent.getComboBox().getEditor().getEditorComponent();
editorComponent.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(final DocumentEvent e) {
final String sdkHomePath = dartSdkPathComponent.getComboBox().getEditor().getItem().toString().trim();
versionLabel.setText(sdkHomePath.isEmpty() ? "" : getSdkVersion(sdkHomePath));
if (!isResettingControlsComputable.compute() && isDartSdkHome(sdkHomePath)) {
final String dartiumPath = DartiumUtil.getDartiumPathForSdk(sdkHomePath);
if (dartiumPath != null) {
dartiumPathComponent.getComboBox().getEditor().setItem(FileUtilRt.toSystemDependentName(dartiumPath));
}
}
}
});
dartiumSettingsButton.addActionListener(e -> ShowSettingsUtil.getInstance().editConfigurable(dartiumSettingsButton,
currentDartiumSettingsRetriever.compute()
.createConfigurable()));
// we decided to save one line in settings and always use Dartium in checked mode
//checkedModeCheckBox.addActionListener(new ActionListener() {
// public void actionPerformed(final ActionEvent e) {
// DartiumUtil.setCheckedMode(currentDartiumSettingsRetriever.compute().getEnvironmentVariables(), checkedModeCheckBox.isSelected());
// }
//});
}
@Nullable
public static String getFirstKnownDartSdkPath() {
final String[] knownPaths = PropertiesComponent.getInstance().getValues(DART_SDK_KNOWN_PATHS);
if (knownPaths != null && knownPaths.length > 0 && isDartSdkHome(knownPaths[0])) {
return knownPaths[0];
}
return null;
}
private static void addKnownPathsToCombo(@NotNull final JComboBox combo,
@NotNull final String propertyKey,
@NotNull final BooleanFunction<String> pathChecker) {
final SmartList<String> validPathsForUI = new SmartList<>();
final String currentPath = combo.getEditor().getItem().toString().trim();
if (!currentPath.isEmpty()) {
validPathsForUI.add(currentPath);
}
final String[] knownPaths = PropertiesComponent.getInstance().getValues(propertyKey);
if (knownPaths != null && knownPaths.length > 0) {
for (String path : knownPaths) {
final String pathSD = FileUtil.toSystemDependentName(path);
if (!pathSD.equals(currentPath) && pathChecker.fun(path)) {
validPathsForUI.add(pathSD);
}
}
}
combo.setModel(new DefaultComboBoxModel<>(ArrayUtil.toStringArray(validPathsForUI)));
}
public static void updateKnownSdkPaths(@NotNull final Project project, @NotNull final String newSdkPath) {
final DartSdk oldSdk = DartSdk.getDartSdk(project);
updateKnownPaths(DART_SDK_KNOWN_PATHS, oldSdk == null ? null : oldSdk.getHomePath(), newSdkPath);
}
public static void updateKnownDartiumPaths(@Nullable final String oldPath, @NotNull final String newPath) {
updateKnownPaths(DARTIUM_KNOWN_PATHS, oldPath, newPath);
}
private static void updateKnownPaths(@NotNull final String propertyKey, @Nullable final String oldPath, @NotNull final String newPath) {
final List<String> knownPaths = new ArrayList<>();
final String[] oldKnownPaths = PropertiesComponent.getInstance().getValues(propertyKey);
if (oldKnownPaths != null) {
knownPaths.addAll(Arrays.asList(oldKnownPaths));
}
if (oldPath != null) {
knownPaths.remove(oldPath);
knownPaths.add(0, oldPath);
}
knownPaths.remove(newPath);
knownPaths.add(0, newPath);
if (knownPaths.isEmpty()) {
PropertiesComponent.getInstance().unsetValue(propertyKey);
}
else {
PropertiesComponent.getInstance().setValues(propertyKey, ArrayUtil.toStringArray(knownPaths));
}
}
@Nullable
public static String getErrorMessageIfWrongSdkRootPath(final @NotNull String sdkRootPath) {
if (sdkRootPath.isEmpty()) return DartBundle.message("error.path.to.sdk.not.specified");
final File sdkRoot = new File(sdkRootPath);
if (!sdkRoot.isDirectory()) return DartBundle.message("error.folder.specified.as.sdk.not.exists");
if (!isDartSdkHome(sdkRootPath)) return DartBundle.message("error.sdk.not.found.in.specified.location");
return null;
}
public static String getDartExePath(final @NotNull DartSdk sdk) {
return sdk.getHomePath() + (SystemInfo.isWindows ? "/bin/dart.exe" : "/bin/dart");
}
public static String getPubPath(final @NotNull DartSdk sdk) {
return getPubPath(sdk.getHomePath());
}
public static String getPubPath(final @NotNull String sdkRoot) {
return sdkRoot + (SystemInfo.isWindows ? "/bin/pub.bat" : "/bin/pub");
}
public static String getPubSnapshotPath(@NotNull final DartSdk sdk) {
return sdk.getHomePath() + "/bin/snapshots/pub.dart.snapshot";
}
}