6a9274a5a610a1ee5e816dd43dabc87b7588b8c8
[idea/community.git] / platform / lang-impl / src / com / intellij / openapi / util / SwitchBootJdkAction.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.util;
17
18 import com.intellij.openapi.actionSystem.AnAction;
19 import com.intellij.openapi.actionSystem.AnActionEvent;
20 import com.intellij.openapi.actionSystem.Presentation;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.ApplicationNamesInfo;
23 import com.intellij.openapi.application.PathManager;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.project.DumbAware;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.ui.ComboBox;
28 import com.intellij.openapi.ui.DialogWrapper;
29 import com.intellij.openapi.util.io.FileUtil;
30 import com.intellij.ui.JBColor;
31 import com.intellij.ui.ListCellRendererWrapper;
32 import com.intellij.ui.components.JBLabel;
33 import com.intellij.util.JdkBundle;
34 import com.intellij.util.JdkBundleList;
35 import com.intellij.util.PlatformUtils;
36 import org.jetbrains.annotations.NonNls;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39
40 import javax.swing.*;
41 import javax.swing.event.ListDataEvent;
42 import javax.swing.event.ListDataListener;
43 import java.io.*;
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.Locale;
47
48 /**
49  * @author denis
50  */
51 public class SwitchBootJdkAction extends AnAction implements DumbAware {
52   @NonNls private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.SwitchBootJdkAction");
53   @NonNls private static final String productJdkConfigFileName = getExecutable() + ".jdk";
54   @NonNls private static final File productJdkConfigFile = new File(PathManager.getConfigPath(), productJdkConfigFileName);
55   @NonNls private static final File bundledJdkFile = getBundledJDKFile();
56
57
58
59   @NotNull
60   private static File getBundledJDKFile() {
61     StringBuilder bundledJDKPath = new StringBuilder("jre");
62     if (SystemInfo.isMac) {
63       bundledJDKPath.append(File.separator).append("jdk");
64     }
65     return new File(bundledJDKPath.toString());
66   }
67
68   @Override
69   public void update(AnActionEvent e) {
70     Presentation presentation = e.getPresentation();
71     if (!(SystemInfo.isMac || SystemInfo.isLinux)) {
72       presentation.setEnabledAndVisible(false);
73       return;
74     }
75     e.getPresentation().setText("Switch Boot JDK");
76   }
77
78   private static List<JdkBundle> getBundlesFromFile(@NotNull File fileWithBundles) {
79     List<JdkBundle> list = new ArrayList<JdkBundle>();
80     try {
81       for (String line : FileUtil.loadLines(fileWithBundles, "UTF-8")) {
82         File storedFile = new File(line);
83         final boolean isBundled = !storedFile.isAbsolute();
84         File actualFile = isBundled ? new File(PathManager.getHomePath(), storedFile.getPath()) : storedFile;
85         if (actualFile.exists()) {
86           list.add(JdkBundle.createBundle(storedFile, false, isBundled));
87         }
88       }
89     } catch (IllegalStateException e) {
90       // The device builders can throw IllegalStateExceptions if
91       // build gets called before everything is properly setup
92       LOG.error(e);
93     } catch (IOException e) {
94       LOG.error("Error reading JDK bundles", e);
95     }
96     return list;
97   }
98
99   @Override
100   public void actionPerformed(AnActionEvent event) {
101
102     if (!productJdkConfigFile.exists()) {
103       try {
104         if (!productJdkConfigFile.createNewFile()){
105           LOG.error("Could not create " + productJdkConfigFileName + " productJdkConfigFile");
106           return;
107         }
108       }
109       catch (IOException e) {
110         LOG.error(e);
111         return;
112       }
113     }
114
115     SwitchBootJdkDialog dialog = new SwitchBootJdkDialog(null, getBundlesFromFile(productJdkConfigFile));
116     if (dialog.showAndGet()) {
117       File selectedJdkBundleFile = dialog.getSelectedFile();
118       FileWriter fooWriter = null;
119       try {
120         //noinspection IOResourceOpenedButNotSafelyClosed
121         fooWriter = new FileWriter(productJdkConfigFile, false);
122         fooWriter.write(selectedJdkBundleFile.getPath());
123       }
124       catch (IOException e) {
125         LOG.error(e);
126       }
127       finally {
128         try {
129           if (fooWriter != null) {
130             fooWriter.close();
131           }
132         }
133         catch (IOException e) {
134           LOG.error(e);
135         }
136       }
137       ApplicationManager.getApplication().restart();
138     }
139   }
140
141   private static class SwitchBootJdkDialog extends DialogWrapper {
142
143     @NotNull private final ComboBox myComboBox;
144
145     private SwitchBootJdkDialog(@Nullable Project project, final List<JdkBundle> jdkBundlesList) {
146       super(project, false);
147
148       final JdkBundleList pathsList = findJdkPaths();
149
150       myComboBox = new ComboBox();
151
152       DefaultComboBoxModel model = new DefaultComboBoxModel();
153
154       for (JdkBundle jdkBundlePath : pathsList.toArrayList()) {
155         //noinspection unchecked
156         model.addElement(jdkBundlePath);
157       }
158
159       model.addListDataListener(new ListDataListener() {
160         @Override
161         public void intervalAdded(ListDataEvent e) { }
162
163         @Override
164         public void intervalRemoved(ListDataEvent e) { }
165
166         @Override
167         public void contentsChanged(ListDataEvent e) {
168           setOKActionEnabled(!((JdkBundle)myComboBox.getSelectedItem()).isBoot());
169         }
170       });
171
172       //noinspection unchecked
173       myComboBox.setModel(model);
174
175       myComboBox.setRenderer(new ListCellRendererWrapper() {
176         @Override
177         public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
178           if (value != null) {
179             JdkBundle jdkBundleDescriptor = ((JdkBundle)value);
180             if (jdkBundleDescriptor.isBoot()) {
181               setForeground(JBColor.DARK_GRAY);
182             }
183             setText(jdkBundleDescriptor.getVisualRepresentation());
184           }
185           else {
186             if (LOG.isDebugEnabled()) {
187               LOG.debug("Null value has been passed to a cell renderer. Available JDKs count: " + pathsList.toArrayList().size());
188               StringBuilder jdkNames = new StringBuilder();
189               for (JdkBundle jdkBundlePath : pathsList.toArrayList()) {
190                 if (!jdkBundlesList.isEmpty()) {
191                   continue;
192                 }
193                 jdkNames.append(jdkBundlePath.getVisualRepresentation()).append("; ");
194               }
195               if (LOG.isDebugEnabled()) {
196                 LOG.debug("Available JDKs names: " + jdkNames.toString());
197               }
198             }
199           }
200         }
201       });
202
203       setTitle("Switch IDE Boot JDK");
204       setOKActionEnabled(false); // First item is a boot jdk
205       init();
206     }
207
208     @Nullable
209     @Override
210     protected JComponent createNorthPanel() {
211       return new JBLabel("Select Boot JDK");
212     }
213
214     @Nullable
215     @Override
216     protected JComponent createCenterPanel() {
217       return myComboBox;
218     }
219
220     @Nullable
221     @Override
222     public JComponent getPreferredFocusedComponent() {
223       return myComboBox;
224     }
225
226     public File getSelectedFile() {
227       return ((JdkBundle)myComboBox.getSelectedItem()).getLocation();
228     }
229   }
230
231   private static final String STANDARD_JDK_LOCATION_ON_MAC_OS_X = "/Library/Java/JavaVirtualMachines/";
232   private static final String [] STANDARD_JVM_LOCATIONS_ON_LINUX = new String[] {
233     "/usr/lib/jvm/", // Ubuntu
234     "/usr/java/"     // Fedora
235   };
236
237   private static final Version JDK8_VERSION = new Version(1, 8, 0);
238
239   @NotNull
240   private static JdkBundleList findJdkPaths() {
241     JdkBundle bootJdk = JdkBundle.createBoot();
242
243     JdkBundleList jdkBundleList = new JdkBundleList();
244     if (bootJdk != null) {
245       jdkBundleList.addBundle(bootJdk, true);
246     }
247
248     if (new File(PathManager.getHomePath() + File.separator + bundledJdkFile).exists()) {
249       JdkBundle bundledJdk = JdkBundle.createBundle(bundledJdkFile, false, true);
250       if (bundledJdk != null) {
251         jdkBundleList.addBundle(bundledJdk, true);
252       }
253     }
254
255     if (SystemInfo.isMac) {
256       jdkBundleList.addBundlesFromLocation(STANDARD_JDK_LOCATION_ON_MAC_OS_X, JDK8_VERSION, null);
257     }
258     else if (SystemInfo.isLinux) {
259       for (String location : STANDARD_JVM_LOCATIONS_ON_LINUX) {
260         jdkBundleList.addBundlesFromLocation(location, JDK8_VERSION, null);
261       }
262     }
263
264     return jdkBundleList;
265   }
266
267   @NotNull
268   private static String getExecutable() {
269     final String executable = System.getProperty("idea.executable");
270     return executable != null ? executable : ApplicationNamesInfo.getInstance().getProductName().toLowerCase(Locale.US);
271   }
272 }