WI-629 Help userto download JDBC drivers
[fedora-idea.git] / platform / platform-api / src / com / intellij / ide / BrowserUtil.java
blob9b384ebc9b1d6ea0f60c990b7d1f1cde73b05346
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.ide;
18 import com.intellij.CommonBundle;
19 import com.intellij.openapi.application.*;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.progress.ProgressIndicator;
22 import com.intellij.openapi.progress.Task;
23 import com.intellij.openapi.ui.Messages;
24 import com.intellij.openapi.util.SystemInfo;
25 import com.intellij.openapi.util.io.FileUtil;
26 import com.intellij.openapi.vfs.JarFileSystem;
27 import com.intellij.openapi.vfs.VfsUtil;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.vfs.VirtualFileManager;
30 import com.intellij.util.io.ZipUtil;
31 import com.intellij.util.ui.OptionsDialog;
32 import org.jetbrains.annotations.NonNls;
33 import org.jetbrains.annotations.NotNull;
35 import javax.swing.*;
36 import java.awt.*;
37 import java.io.*;
38 import java.net.URL;
39 import java.util.Arrays;
40 import java.util.HashSet;
41 import java.util.Set;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 import java.util.zip.ZipEntry;
45 import java.util.zip.ZipFile;
47 public class BrowserUtil {
48 private static final Logger LOG = Logger.getInstance("#" + BrowserUtil.class.getName());
50 // The pattern for 'scheme' mainly according to RFC1738.
51 // We have to violate the RFC since we need to distinguish
52 // real schemes from local Windows paths; The only difference
53 // with RFC is that we do not allow schemes with length=1 (in other case
54 // local paths like "C:/temp/index.html" whould be erroneously interpreted as
55 // external URLs.)
56 @NonNls private static final Pattern ourExternalPrefix = Pattern.compile("^[\\w\\+\\.\\-]{2,}:");
57 private static final Pattern ourAnchorsuffix = Pattern.compile("#(.*)$");
59 private BrowserUtil() {
62 public static boolean isAbsoluteURL(String url) {
63 return ourExternalPrefix.matcher(url.toLowerCase()).find();
66 public static String getDocURL(String url) {
67 Matcher anchorMatcher = ourAnchorsuffix.matcher(url);
69 if (anchorMatcher.find()) {
70 return anchorMatcher.reset().replaceAll("");
73 return url;
76 public static URL getURL(String url) throws java.net.MalformedURLException {
77 if (!isAbsoluteURL(url)) {
78 return new URL("file", "", url);
81 return VfsUtil.convertToURL(url);
84 private static void launchBrowser(final String url, String[] command) {
85 try {
86 URL curl = BrowserUtil.getURL(url);
88 if (curl != null) {
89 final String urlString = curl.toString();
90 String[] commandLine;
91 if (SystemInfo.isWindows && isUseDefaultBrowser()) {
92 commandLine = new String[command.length + 2];
93 System.arraycopy(command, 0, commandLine, 0, command.length);
94 commandLine[commandLine.length - 2] = "\"\"";
95 commandLine[commandLine.length - 1] = "\"" + redirectUrl(url, urlString) + "\"";
97 else {
98 commandLine = new String[command.length + 1];
99 System.arraycopy(command, 0, commandLine, 0, command.length);
100 commandLine[commandLine.length - 1] = escapeUrl(urlString);
102 Runtime.getRuntime().exec(commandLine);
104 else {
105 showErrorMessage(IdeBundle.message("error.malformed.url", url), CommonBundle.getErrorTitle());
108 catch (final IOException e) {
109 showErrorMessage(IdeBundle.message("error.cannot.start.browser", e.getMessage()),
110 CommonBundle.getErrorTitle());
115 * This method works around Windows 'start' command behaivor of dropping anchors from the url for local urls.
117 private static String redirectUrl(String url, @NonNls String urlString) throws IOException {
118 if (url.indexOf('&') == -1 && (!urlString.startsWith("file:") || urlString.indexOf("#") == -1)) return urlString;
120 File redirect = File.createTempFile("redirect", ".html");
121 redirect.deleteOnExit();
122 FileWriter writer = new FileWriter(redirect);
123 writer.write("<html><head></head><body><script type=\"text/javascript\">window.location=\"" + url + "\";</script></body></html>");
124 writer.close();
125 return VfsUtil.pathToUrl(redirect.getAbsolutePath());
128 private static boolean isUseDefaultBrowser() {
129 Application application = ApplicationManager.getApplication();
130 if (application == null) {
131 return true;
133 else {
134 return getGeneralSettingsInstance().isUseDefaultBrowser();
138 private static void showErrorMessage(final String message, final String title) {
139 final Application app = ApplicationManager.getApplication();
140 if (app == null) {
141 return; // Not started yet. Not able to show message up. (Could happen in License panel under Linux).
144 Runnable runnable = new Runnable() {
145 public void run() {
146 Messages.showMessageDialog(message,
147 title,
148 Messages.getErrorIcon());
152 if (app.isDispatchThread()) {
153 runnable.run();
155 else {
156 app.invokeLater(runnable, ModalityState.NON_MODAL);
160 private static void launchBrowserUsingStandardWay(final String url) {
161 String[] command;
162 try {
163 String browserPath = getGeneralSettingsInstance().getBrowserPath();
164 if (browserPath == null || browserPath.trim().length() == 0) {
165 showErrorMessage(IdeBundle.message("error.please.specify.path.to.web.browser"),
166 IdeBundle.message("title.browser.not.found"));
167 return;
170 command = new String[]{browserPath};
172 catch (NullPointerException e) {
173 // todo: fix the possible problem on startup, see SCR #35066
174 command = getDefaultBrowserCommand();
175 if (command == null) {
176 showErrorMessage(IdeBundle.message("error.please.open.url.manually", url, ApplicationNamesInfo.getInstance().getProductName()),
177 IdeBundle.message("title.browser.path.not.found"));
178 return;
181 // We do not need to check browserPath under Win32
183 launchBrowser(url, command);
186 private static GeneralSettings getGeneralSettingsInstance() {
187 final GeneralSettings settings = GeneralSettings.getInstance();
188 if (settings != null) return settings;
189 return new GeneralSettings();
192 public static void launchBrowser(String url, String name) {
193 if (url.startsWith("jar:")) {
194 url = extractFiles(url);
195 if (url == null) return;
197 if (canStartDefaultBrowser() && isUseDefaultBrowser()) {
198 launchBrowser(url, getDefaultBrowserCommand());
200 else {
201 launchBrowserUsingStandardWay(url);
205 @NotNull
206 public static String escapeUrl(@NotNull @NonNls String url) {
207 if (SystemInfo.isWindows) {
208 return url.indexOf(' ') > 0? "\"" + url + "\"" : url;
210 else {
211 return url.replaceAll(" ", "%20");
215 private static String extractFiles(String url) {
216 try {
217 int sharpPos = url.indexOf('#');
218 String anchor = "";
219 if (sharpPos != -1) {
220 anchor = url.substring(sharpPos);
221 url = url.substring(0, sharpPos);
224 VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
225 if (file == null || !(file.getFileSystem() instanceof JarFileSystem)) return null;
227 JarFileSystem jarFileSystem = (JarFileSystem)file.getFileSystem();
228 VirtualFile jarVirtualFile = jarFileSystem.getVirtualFileForJar(file);
230 String targetFilePath = file.getPath();
231 String targetFileRelativePath = targetFilePath.substring(
232 targetFilePath.indexOf(JarFileSystem.JAR_SEPARATOR) + JarFileSystem.JAR_SEPARATOR.length());
234 String jarVirtualFileLocationHash = jarVirtualFile.getName() + Integer.toHexString(jarVirtualFile.getUrl().hashCode());
235 final File outputDir = new File(getExtractedFilesDir(), jarVirtualFileLocationHash);
237 final String currentTimestamp = String.valueOf(new File(jarVirtualFile.getPath()).lastModified());
238 final File timestampFile = new File(outputDir, ".idea.timestamp");
240 String previousTimestamp = null;
241 if (timestampFile.exists()) {
242 previousTimestamp = new String(FileUtil.loadFileText(timestampFile));
245 if (!currentTimestamp.equals(previousTimestamp)) {
246 ConfirmExtractDialog dialog = new ConfirmExtractDialog();
247 if (dialog.isToBeShown()) {
248 dialog.show();
249 if (!dialog.isOK()) return null;
252 final ZipFile jarFile = jarFileSystem.getJarFile(file);
253 ZipEntry entry = jarFile.getEntry(targetFileRelativePath);
254 if (entry == null) return null;
255 InputStream is = jarFile.getInputStream(entry);
256 try {
257 ZipUtil.extractEntry(entry, is, outputDir);
259 finally {
260 is.close();
263 ApplicationManager.getApplication().invokeLater(new Runnable() {
264 public void run() {
265 new Task.Backgroundable(null, "Extracting files...", true) {
266 public void run(@NotNull final ProgressIndicator indicator) {
267 final int size = jarFile.size();
268 final int[] counter = new int[]{0};
270 class MyFilter implements FilenameFilter {
271 private final Set<File> myImportantDirs = new HashSet<File>(
272 Arrays.asList(outputDir, new File(outputDir, "resources")));
273 private final boolean myImportantOnly;
275 private MyFilter(boolean importantOnly) {
276 myImportantOnly = importantOnly;
279 public boolean accept(File dir, String name) {
280 indicator.checkCanceled();
281 boolean result = myImportantOnly == myImportantDirs.contains(dir);
282 if (result) {
283 indicator.setFraction(((double)counter[0]) / size);
284 counter[0]++;
286 return result;
290 try {
291 ZipUtil.extract(jarFile, outputDir, new MyFilter(true));
292 ZipUtil.extract(jarFile, outputDir, new MyFilter(false));
293 FileUtil.writeToFile(timestampFile, currentTimestamp.getBytes());
295 catch (IOException ignore) {
298 }.queue();
303 return VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(new File(outputDir, targetFileRelativePath).getPath())) + anchor;
305 catch (IOException e) {
306 LOG.warn(e);
307 Messages.showErrorDialog("Cannot extract files: " + e.getMessage(), "Error");
308 return null;
312 public static void clearExtractedFiles() {
313 FileUtil.delete(getExtractedFilesDir());
316 private static File getExtractedFilesDir() {
317 return new File(PathManager.getSystemPath(), "ExtractedFiles");
320 public static void launchBrowser(@NonNls final String url) {
321 launchBrowser(url, (String)null);
324 @NonNls
325 private static String[] getDefaultBrowserCommand() {
326 if (SystemInfo.isWindows9x) {
327 return new String[]{"command.com", "/c", "start"};
329 else if (SystemInfo.isWindows) {
330 return new String[]{"cmd.exe", "/c", "start"};
332 else if (SystemInfo.isMac) {
333 return new String[]{"open"};
335 else if (SystemInfo.isUnix) {
336 return new String[]{"mozilla"};
338 else {
339 return null;
343 public static boolean canStartDefaultBrowser() {
344 if (SystemInfo.isMac) {
345 return true;
348 if (SystemInfo.isWindows) {
349 return true;
352 return false;
355 private static class ConfirmExtractDialog extends OptionsDialog {
356 private ConfirmExtractDialog() {
357 super(null);
358 setTitle("Confirmation");
359 init();
362 protected boolean isToBeShown() {
363 return getGeneralSettingsInstance().isConfirmExtractFiles();
366 protected void setToBeShown(boolean value, boolean onOk) {
367 getGeneralSettingsInstance().setConfirmExtractFiles(value);
370 protected boolean shouldSaveOptionsOnCancel() {
371 return true;
374 protected Action[] createActions() {
375 setOKButtonText(CommonBundle.getYesButtonText());
376 return new Action[]{getOKAction(), getCancelAction()};
379 protected JComponent createCenterPanel() {
380 JPanel panel = new JPanel(new BorderLayout());
381 String message = "The files are inside an archive, do you want them to be extracted?";
382 JLabel label = new JLabel(message);
384 label.setIconTextGap(10);
385 label.setIcon(Messages.getQuestionIcon());
387 panel.add(label, BorderLayout.CENTER);
388 panel.add(Box.createVerticalStrut(10), BorderLayout.SOUTH);
390 return panel;