8571ac0e120fc91661fccfbbdb8b3f1f0f28171a
[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 =
54     getExecutable() + (SystemInfo.isWindows ? ((SystemInfo.is64Bit) ? "64.exe.jdk" : ".exe.jdk") : ".jdk");
55   @NonNls private static final File productJdkConfigFile = new File(PathManager.getConfigPath(), productJdkConfigFileName);
56   @NonNls private static final File bundledJdkFile = getBundledJDKFile();
57
58
59
60   @NotNull
61   private static File getBundledJDKFile() {
62     StringBuilder bundledJDKPath = new StringBuilder("jre");
63     if (SystemInfo.isMac) {
64       bundledJDKPath.append(File.separator).append("jdk");
65     }
66     return new File(bundledJDKPath.toString());
67   }
68
69   @Override
70   public void update(AnActionEvent e) {
71     e.getPresentation().setText("Switch Boot JDK");
72   }
73
74   private static List<JdkBundle> getBundlesFromFile(@NotNull File fileWithBundles) {
75     List<JdkBundle> list = new ArrayList<JdkBundle>();
76     try {
77       for (String line : FileUtil.loadLines(fileWithBundles, "UTF-8")) {
78         File storedFile = new File(line);
79         final boolean isBundled = !storedFile.isAbsolute();
80         File actualFile = isBundled ? new File(PathManager.getHomePath(), storedFile.getPath()) : storedFile;
81         if (actualFile.exists()) {
82           list.add(JdkBundle.createBundle(storedFile, false, isBundled));
83         }
84       }
85     } catch (IllegalStateException e) {
86       // The device builders can throw IllegalStateExceptions if
87       // build gets called before everything is properly setup
88       LOG.error(e);
89     } catch (IOException e) {
90       LOG.error("Error reading JDK bundles", e);
91     }
92     return list;
93   }
94
95   @Override
96   public void actionPerformed(AnActionEvent event) {
97
98     if (!productJdkConfigFile.exists()) {
99       try {
100         if (!productJdkConfigFile.createNewFile()){
101           LOG.error("Could not create " + productJdkConfigFileName + " productJdkConfigFile");
102           return;
103         }
104       }
105       catch (IOException e) {
106         LOG.error(e);
107         return;
108       }
109     }
110
111     SwitchBootJdkDialog dialog = new SwitchBootJdkDialog(null, getBundlesFromFile(productJdkConfigFile));
112     if (dialog.showAndGet()) {
113       File selectedJdkBundleFile = dialog.getSelectedFile();
114       FileWriter fooWriter = null;
115       try {
116         //noinspection IOResourceOpenedButNotSafelyClosed
117         fooWriter = new FileWriter(productJdkConfigFile, false);
118         fooWriter.write(selectedJdkBundleFile.getPath());
119       }
120       catch (IOException e) {
121         LOG.error(e);
122       }
123       finally {
124         try {
125           if (fooWriter != null) {
126             fooWriter.close();
127           }
128         }
129         catch (IOException e) {
130           LOG.error(e);
131         }
132       }
133       ApplicationManager.getApplication().restart();
134     }
135   }
136
137   private static class SwitchBootJdkDialog extends DialogWrapper {
138
139     @NotNull private final ComboBox myComboBox;
140
141     private SwitchBootJdkDialog(@Nullable Project project, final List<JdkBundle> jdkBundlesList) {
142       super(project, false);
143
144       final JdkBundleList pathsList = findJdkPaths();
145
146       myComboBox = new ComboBox();
147
148       DefaultComboBoxModel model = new DefaultComboBoxModel();
149
150       for (JdkBundle jdkBundlePath : pathsList.toArrayList()) {
151         //noinspection unchecked
152         model.addElement(jdkBundlePath);
153       }
154
155       model.addListDataListener(new ListDataListener() {
156         @Override
157         public void intervalAdded(ListDataEvent e) { }
158
159         @Override
160         public void intervalRemoved(ListDataEvent e) { }
161
162         @Override
163         public void contentsChanged(ListDataEvent e) {
164           setOKActionEnabled(!((JdkBundle)myComboBox.getSelectedItem()).isBoot());
165         }
166       });
167
168       //noinspection unchecked
169       myComboBox.setModel(model);
170
171       myComboBox.setRenderer(new ListCellRendererWrapper() {
172         @Override
173         public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
174           if (value != null) {
175             JdkBundle jdkBundleDescriptor = ((JdkBundle)value);
176             if (jdkBundleDescriptor.isBoot()) {
177               setForeground(JBColor.DARK_GRAY);
178             }
179             setText(jdkBundleDescriptor.getVisualRepresentation());
180           }
181           else {
182             if (LOG.isDebugEnabled()) {
183               LOG.debug("Null value has been passed to a cell renderer. Available JDKs count: " + pathsList.toArrayList().size());
184               StringBuilder jdkNames = new StringBuilder();
185               for (JdkBundle jdkBundlePath : pathsList.toArrayList()) {
186                 if (!jdkBundlesList.isEmpty()) {
187                   continue;
188                 }
189                 jdkNames.append(jdkBundlePath.getVisualRepresentation()).append("; ");
190               }
191               if (LOG.isDebugEnabled()) {
192                 LOG.debug("Available JDKs names: " + jdkNames.toString());
193               }
194             }
195           }
196         }
197       });
198
199       setTitle("Switch IDE Boot JDK");
200       setOKActionEnabled(false); // First item is a boot jdk
201       init();
202     }
203
204     @Nullable
205     @Override
206     protected JComponent createNorthPanel() {
207       return new JBLabel("Select Boot JDK");
208     }
209
210     @Nullable
211     @Override
212     protected JComponent createCenterPanel() {
213       return myComboBox;
214     }
215
216     @Nullable
217     @Override
218     public JComponent getPreferredFocusedComponent() {
219       return myComboBox;
220     }
221
222     public File getSelectedFile() {
223       return ((JdkBundle)myComboBox.getSelectedItem()).getLocation();
224     }
225   }
226
227   private static final String STANDARD_JDK_LOCATION_ON_MAC_OS_X = "/Library/Java/JavaVirtualMachines/";
228   private static final String [] STANDARD_JVM_LOCATIONS_ON_LINUX = new String[] {
229     "/usr/lib/jvm/", // Ubuntu
230     "/usr/java/"     // Fedora
231   };
232   private static final String STANDARD_JVM_X64_LOCATIONS_ON_WINDOWS = "Program Files/Java";
233
234   private static final String STANDARD_JVM_X86_LOCATIONS_ON_WINDOWS = "Program Files (x86)/Java";
235
236   private static final Version JDK8_VERSION = new Version(1, 8, 0);
237
238   @NotNull
239   private static JdkBundleList findJdkPaths() {
240     JdkBundle bootJdk = JdkBundle.createBoot();
241
242     JdkBundleList jdkBundleList = new JdkBundleList();
243     if (bootJdk != null) {
244       jdkBundleList.addBundle(bootJdk, true);
245     }
246
247     if (new File(PathManager.getHomePath() + File.separator + bundledJdkFile).exists()) {
248       JdkBundle bundledJdk = JdkBundle.createBundle(bundledJdkFile, false, true);
249       if (bundledJdk != null) {
250         jdkBundleList.addBundle(bundledJdk, true);
251       }
252     }
253
254     if (SystemInfo.isMac) {
255       jdkBundleList.addBundlesFromLocation(STANDARD_JDK_LOCATION_ON_MAC_OS_X, JDK8_VERSION, null);
256     }
257     else if (SystemInfo.isLinux) {
258       for (String location : STANDARD_JVM_LOCATIONS_ON_LINUX) {
259         jdkBundleList.addBundlesFromLocation(location, JDK8_VERSION, null);
260       }
261     }
262     else if (SystemInfo.isWindows) {
263       for (File root : File.listRoots()) {
264         if (SystemInfo.is32Bit) {
265           jdkBundleList.addBundlesFromLocation(new File(root, STANDARD_JVM_X86_LOCATIONS_ON_WINDOWS).getAbsolutePath(), JDK8_VERSION, null);
266         }
267         else {
268           jdkBundleList.addBundlesFromLocation(new File(root, STANDARD_JVM_X64_LOCATIONS_ON_WINDOWS).getAbsolutePath(), JDK8_VERSION, null);
269         }
270       }
271     }
272
273     return jdkBundleList;
274   }
275
276   @NotNull
277   private static String getExecutable() {
278     final String executable = System.getProperty("idea.executable");
279     return executable != null ? executable : ApplicationNamesInfo.getInstance().getProductName().toLowerCase(Locale.US);
280   }
281 }