should be possible to create idea jdk over idea compiled with jdk version lesser...
[fedora-idea.git] / plugins / devkit / src / projectRoots / IdeaJdk.java
blob9e40859e180853f58f0b9bd70156eb945b31a538
1 /*
2 * Copyright 2000-2005 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 org.jetbrains.idea.devkit.projectRoots;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.application.PathManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.projectRoots.*;
22 import com.intellij.openapi.roots.JavadocOrderRootType;
23 import com.intellij.openapi.roots.OrderRootType;
24 import com.intellij.openapi.ui.Messages;
25 import com.intellij.openapi.util.IconLoader;
26 import com.intellij.openapi.util.InvalidDataException;
27 import com.intellij.openapi.util.SystemInfo;
28 import com.intellij.openapi.util.WriteExternalException;
29 import com.intellij.openapi.util.io.FileUtil;
30 import com.intellij.openapi.vfs.JarFileSystem;
31 import com.intellij.openapi.vfs.LocalFileSystem;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.openapi.vfs.VirtualFileManager;
34 import com.intellij.util.cls.BytePointer;
35 import com.intellij.util.cls.ClsFormatException;
36 import com.intellij.util.cls.ClsUtil;
37 import org.jdom.Element;
38 import org.jetbrains.annotations.NonNls;
39 import org.jetbrains.annotations.Nullable;
40 import org.jetbrains.idea.devkit.DevKitBundle;
42 import javax.swing.*;
43 import java.io.File;
44 import java.io.FileFilter;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.List;
49 /**
50 * User: anna
51 * Date: Nov 22, 2004
53 public class IdeaJdk extends SdkType implements JavaSdkType {
54 private static final Icon ADD_SDK = IconLoader.getIcon("/add_sdk.png");
55 private static final Icon SDK_OPEN = IconLoader.getIcon("/sdk_open.png");
56 private static final Icon SDK_CLOSED = IconLoader.getIcon("/sdk_closed.png");
58 private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.projectRoots.IdeaJdk");
59 @NonNls private static final String JAVA_HOME_PROPERTY = "java.home";
60 @NonNls private static final String LIB_DIR_NAME = "lib";
61 @NonNls private static final String SRC_DIR_NAME = "src";
62 @NonNls private static final String JRE_DIR_NAME = "jre";
63 @NonNls private static final String PLUGINS_DIR = "plugins";
64 @NonNls private static final String JAVAEE_DIR = "JavaEE";
65 @NonNls private static final String JSF_DIR = "JSF";
66 @NonNls private static final String PERSISTENCE_SUPPORT = "PersistenceSupport";
67 @NonNls private static final String DATABASE_DIR = "DatabaseSupport";
68 @NonNls private static final String CSS_DIR = "css";
70 public IdeaJdk() {
71 super("IDEA JDK");
74 public Icon getIcon() {
75 return SDK_CLOSED;
78 public Icon getIconForExpandedTreeNode() {
79 return SDK_OPEN;
82 public Icon getIconForAddAction() {
83 return ADD_SDK;
86 public String suggestHomePath() {
87 return PathManager.getHomePath().replace(File.separatorChar, '/');
90 public boolean isValidSdkHome(String path) {
91 if (isFromIDEAProject(path)) {
92 return true;
94 File home = new File(path);
95 if (!home.exists()) {
96 return false;
98 if (getBuildNumber(path) == null || getOpenApiJar(path) == null) {
99 return false;
101 return true;
104 private static File getOpenApiJar(String home) {
105 @NonNls final String openapiJar = "openapi.jar";
106 @NonNls final String platformApiJar = "platform-api.jar";
107 final File libDir = new File(home, LIB_DIR_NAME);
108 File f = new File(libDir, openapiJar);
109 if (f.exists()) return f;
110 f = new File(libDir, platformApiJar);
111 if (f.exists()) return f;
112 return null;
115 public static boolean isFromIDEAProject(String path) {
116 File home = new File(path);
117 File[] openapiDir = home.listFiles(new FileFilter() {
118 public boolean accept(File pathname) {
119 @NonNls final String name = pathname.getName();
120 if (name.equals("openapi") && pathname.isDirectory()) return true; //todo
121 return false;
124 return openapiDir != null && openapiDir.length != 0;
127 @Nullable
128 public final String getVersionString(final Sdk sdk) {
129 final Sdk internalJavaSdk = getInternalJavaSdk(sdk);
130 return internalJavaSdk != null ? internalJavaSdk.getVersionString() : null;
133 @Nullable
134 private static Sdk getInternalJavaSdk(final Sdk sdk) {
135 final SdkAdditionalData data = sdk.getSdkAdditionalData();
136 if (data instanceof Sandbox) {
137 return ((Sandbox)data).getJavaSdk();
139 return null;
142 public String suggestSdkName(String currentSdkName, String sdkHome) {
143 @NonNls final String productName;
144 if (new File(sdkHome, "lib/rubymine.jar").exists()) {
145 productName = "RubyMine ";
147 else {
148 productName = "IDEA ";
151 String buildNumber = getBuildNumber(sdkHome);
152 return productName + (buildNumber != null ? buildNumber : "");
155 @Nullable
156 private static String getBuildNumber(String ideaHome) {
157 try {
158 @NonNls final String buildTxt = "/build.txt";
159 return new String(FileUtil.loadFileText(new File(ideaHome + buildTxt))).trim();
161 catch (IOException e) {
162 return null;
166 private static VirtualFile[] getIdeaLibrary(String home) {
167 ArrayList<VirtualFile> result = new ArrayList<VirtualFile>();
168 appendIdeaLibrary(home + File.separator + LIB_DIR_NAME, "idea.jar", result);
169 appendIdeaLibrary(home + File.separator + PLUGINS_DIR + File.separator + JAVAEE_DIR + File.separator + LIB_DIR_NAME, "javaee-impl.jar",
170 result);
171 appendIdeaLibrary(home + File.separator + PLUGINS_DIR + File.separator + JSF_DIR + File.separator + LIB_DIR_NAME, "jsf-impl.jar", result);
172 appendIdeaLibrary(home + File.separator + PLUGINS_DIR + File.separator + PERSISTENCE_SUPPORT + File.separator + LIB_DIR_NAME, "persistence-impl.jar", result);
173 appendIdeaLibrary(home + File.separator + PLUGINS_DIR + File.separator + DATABASE_DIR + File.separator + LIB_DIR_NAME, "database-impl.jar", result);
174 appendIdeaLibrary(home + File.separator + PLUGINS_DIR + File.separator + CSS_DIR + File.separator + LIB_DIR_NAME, "css.jar", result);
175 return result.toArray(new VirtualFile[result.size()]);
178 private static void appendIdeaLibrary(final String path, @NonNls final String forbidden, final ArrayList<VirtualFile> result) {
179 final JarFileSystem jfs = JarFileSystem.getInstance();
180 final File lib = new File(path);
181 if (lib.isDirectory()) {
182 File[] jars = lib.listFiles();
183 if (jars != null) {
184 for (File jar : jars) {
185 @NonNls String name = jar.getName();
186 if (jar.isFile() && !name.equals(forbidden) && (name.endsWith(".jar") || name.endsWith(".zip"))) {
187 result.add(jfs.findFileByPath(jar.getPath() + JarFileSystem.JAR_SEPARATOR));
195 public boolean setupSdkPaths(final Sdk sdk, SdkModel sdkModel) {
196 final Sandbox additionalData = (Sandbox)sdk.getSdkAdditionalData();
197 if (additionalData != null) {
198 additionalData.cleanupWatchedRoots();
201 final SdkModificator sdkModificator = sdk.getSdkModificator();
203 final List<String> javaSdks = new ArrayList<String>();
204 final Sdk[] sdks = sdkModel.getSdks();
205 for (Sdk jdk : sdks) {
206 if (isValidInternalJdk(sdk, jdk)) {
207 javaSdks.add(jdk.getName());
210 if (javaSdks.isEmpty()){
211 JDKVersion requiredVer = getRequiredJdkVersion(sdk);
212 if (requiredVer != null) {
213 Messages.showErrorDialog(DevKitBundle.message("no.java.sdk.for.idea.sdk.found", requiredVer), "No Java SDK found");
215 else {
216 Messages.showErrorDialog(DevKitBundle.message("no.idea.sdk.version.found"), "No Java SDK found");
218 return false;
221 final int choice = Messages
222 .showChooseDialog("Select Java SDK to be used as IDEA internal platform",
223 "Select internal Java platform", javaSdks.toArray(new String[javaSdks.size()]), javaSdks.get(0), Messages.getQuestionIcon());
225 if (choice != -1) {
226 final String name = javaSdks.get(choice);
227 final Sdk jdk = sdkModel.findSdk(name);
228 LOG.assertTrue(jdk != null);
229 setupSdkPaths(sdkModificator, sdk.getHomePath(), jdk);
230 sdkModificator.setSdkAdditionalData(new Sandbox(getDefaultSandbox(), jdk, sdk));
231 sdkModificator.setVersionString(jdk.getVersionString());
232 sdkModificator.commitChanges();
233 return true;
235 return false;
238 public static boolean isValidInternalJdk(Sdk ideaSdk, Sdk sdk) {
239 final SdkType sdkType = sdk.getSdkType();
240 if (sdkType instanceof JavaSdk) {
241 final String versionString = sdkType.getVersionString(sdk);
242 JDKVersion requiredJdkVersion = getRequiredJdkVersion(ideaSdk);
243 if (versionString != null && requiredJdkVersion != null) {
244 for (JDKVersion version : JDKVersion.values()) {
245 if (versionString.contains(version.getPresentation())) {
246 return requiredJdkVersion.compareTo(version) <= 0;
249 return true;
252 return false;
255 private static int getIdeaClassFileVersion(final Sdk ideaSdk) {
256 int result = -1;
257 File apiJar = getOpenApiJar(ideaSdk.getHomePath());
258 if (apiJar == null) return -1;
259 final VirtualFile mainClassFile = JarFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(apiJar.getPath()) +
260 "!/com/intellij/psi/PsiManager.class");
261 if (mainClassFile != null) {
262 final BytePointer ptr;
263 try {
264 ptr = new BytePointer(mainClassFile.contentsToByteArray(), 6);
265 result = ClsUtil.readU2(ptr);
267 catch (IOException e) {
268 // ignore
270 catch (ClsFormatException e) {
271 // ignore
274 return result;
277 private static JDKVersion getRequiredJdkVersion(final Sdk ideaSdk) {
278 int classFileVersion = getIdeaClassFileVersion(ideaSdk);
279 JDKVersion requiredJdkVersion = null;
280 switch(classFileVersion) {
281 case 48: requiredJdkVersion = JDKVersion.V1_4; break;
282 case 49: requiredJdkVersion = JDKVersion.V1_5; break;
283 case 50: requiredJdkVersion = JDKVersion.V1_6; break;
285 return requiredJdkVersion;
288 public static void setupSdkPaths(final SdkModificator sdkModificator, final String sdkHome, final Sdk internalJava) {
289 //roots from internal jre
290 addClasses(sdkModificator, internalJava);
291 addDocs(sdkModificator, internalJava);
292 addSources(sdkModificator, internalJava);
293 //roots for openapi and other libs
294 if (!isFromIDEAProject(sdkHome)) {
295 final VirtualFile[] ideaLib = getIdeaLibrary(sdkHome);
296 if (ideaLib != null) {
297 for (VirtualFile aIdeaLib : ideaLib) {
298 sdkModificator.addRoot(aIdeaLib, OrderRootType.CLASSES);
301 addSources(new File(sdkHome), sdkModificator);
302 addDocs(new File(sdkHome), sdkModificator);
306 static String getDefaultSandbox() {
307 @NonNls String defaultSandbox = "";
308 try {
309 defaultSandbox = new File(PathManager.getSystemPath()).getCanonicalPath() + File.separator + "plugins-sandbox";
311 catch (IOException e) {
312 //can't be on running instance
314 return defaultSandbox;
317 private static void addSources(File file, SdkModificator sdkModificator) {
318 final File src = new File(new File(file, LIB_DIR_NAME), SRC_DIR_NAME);
319 if (!src.exists()) return;
320 File[] srcs = src.listFiles(new FileFilter() {
321 public boolean accept(File pathname) {
322 @NonNls final String path = pathname.getPath();
323 //noinspection SimplifiableIfStatement
324 if (path.indexOf("generics") > -1) return false;
325 return path.endsWith(".jar") || path.endsWith(".zip");
328 for (int i = 0; srcs != null && i < srcs.length; i++) {
329 File jarFile = srcs[i];
330 if (jarFile.exists()) {
331 JarFileSystem jarFileSystem = JarFileSystem.getInstance();
332 String path = jarFile.getAbsolutePath().replace(File.separatorChar, '/') + JarFileSystem.JAR_SEPARATOR;
333 jarFileSystem.setNoCopyJarForPath(path);
334 VirtualFile vFile = jarFileSystem.findFileByPath(path);
335 sdkModificator.addRoot(vFile, OrderRootType.SOURCES);
340 private static void addDocs(File file, final SdkModificator sdkModificator) {
341 @NonNls final String help = "help";
342 @NonNls final String openapi = "openapi";
343 final File docFile = new File(new File(file, help), openapi);
344 if (docFile.exists() && docFile.isDirectory()) {
345 ApplicationManager.getApplication().runWriteAction(new Runnable() {
346 public void run() {
347 sdkModificator.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(docFile), JavadocOrderRootType.getInstance());}
350 return;
352 @NonNls final String openapiHelpJar = "openapihelp.jar";
353 File jarfile = new File(new File(file, help), openapiHelpJar);
354 if (jarfile.exists()) {
355 JarFileSystem jarFileSystem = JarFileSystem.getInstance();
356 String path = jarfile.getAbsolutePath().replace(File.separatorChar, '/') + JarFileSystem.JAR_SEPARATOR + openapi;
357 jarFileSystem.setNoCopyJarForPath(path);
358 VirtualFile vFile = jarFileSystem.findFileByPath(path);
359 sdkModificator.addRoot(vFile, JavadocOrderRootType.getInstance());
363 private static void addClasses(SdkModificator sdkModificator, final Sdk javaSdk) {
364 addOrderEntries(OrderRootType.CLASSES, javaSdk, sdkModificator);
367 private static void addDocs(SdkModificator sdkModificator, final Sdk javaSdk) {
368 if (!addOrderEntries(JavadocOrderRootType.getInstance(), javaSdk, sdkModificator) &&
369 SystemInfo.isMac){
370 Sdk[] jdks = ProjectJdkTable.getInstance().getAllJdks();
371 for (Sdk jdk : jdks) {
372 if (jdk.getSdkType() instanceof JavaSdk) {
373 addOrderEntries(JavadocOrderRootType.getInstance(), jdk, sdkModificator);
374 break;
380 private static void addSources(SdkModificator sdkModificator, final Sdk javaSdk) {
381 if (javaSdk != null) {
382 if (!addOrderEntries(OrderRootType.SOURCES, javaSdk, sdkModificator)){
383 if (SystemInfo.isMac) {
384 Sdk[] jdks = ProjectJdkTable.getInstance().getAllJdks();
385 for (Sdk jdk : jdks) {
386 if (jdk.getSdkType() instanceof JavaSdk) {
387 addOrderEntries(OrderRootType.SOURCES, jdk, sdkModificator);
388 break;
392 else {
393 final File jdkHome = new File(javaSdk.getHomePath()).getParentFile();
394 @NonNls final String srcZip = "src.zip";
395 final File jarFile = new File(jdkHome, srcZip);
396 if (jarFile.exists()){
397 JarFileSystem jarFileSystem = JarFileSystem.getInstance();
398 String path = jarFile.getAbsolutePath().replace(File.separatorChar, '/') + JarFileSystem.JAR_SEPARATOR;
399 jarFileSystem.setNoCopyJarForPath(path);
400 sdkModificator.addRoot(jarFileSystem.findFileByPath(path), OrderRootType.SOURCES);
407 private static boolean addOrderEntries(OrderRootType orderRootType, Sdk sdk, SdkModificator toModificator){
408 boolean wasSmthAdded = false;
409 final String[] entries = sdk.getRootProvider().getUrls(orderRootType);
410 for (String entry : entries) {
411 VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(entry);
412 if (virtualFile != null) {
413 toModificator.addRoot(virtualFile, orderRootType);
414 wasSmthAdded = true;
417 return wasSmthAdded;
420 public AdditionalDataConfigurable createAdditionalDataConfigurable(final SdkModel sdkModel, SdkModificator sdkModificator) {
421 final IdeaJdkConfigurable jdkConfigurable = new IdeaJdkConfigurable(sdkModel, sdkModificator);
422 sdkModel.addListener(new SdkModel.Listener() {
423 public void sdkAdded(Sdk sdk) {
424 if (sdk.getSdkType().equals(JavaSdk.getInstance())) {
425 jdkConfigurable.addJavaSdk(sdk);
429 public void beforeSdkRemove(Sdk sdk) {
430 if (sdk.getSdkType().equals(JavaSdk.getInstance())) {
431 jdkConfigurable.removeJavaSdk(sdk);
435 public void sdkChanged(Sdk sdk, String previousName) {
436 if (sdk.getSdkType().equals(JavaSdk.getInstance())) {
437 jdkConfigurable.updateJavaSdkList(sdk, previousName);
441 public void sdkHomeSelected(final Sdk sdk, final String newSdkHome) {
442 if (sdk.getSdkType() instanceof IdeaJdk) {
443 jdkConfigurable.internalJdkUpdate(sdk);
448 return jdkConfigurable;
451 @Nullable
452 public String getBinPath(Sdk sdk) {
453 final Sdk internalJavaSdk = getInternalJavaSdk(sdk);
454 return internalJavaSdk == null ? null : JavaSdk.getInstance().getBinPath(internalJavaSdk);
457 @Nullable
458 public String getToolsPath(Sdk sdk) {
459 final Sdk jdk = getInternalJavaSdk(sdk);
460 if (jdk != null && jdk.getVersionString() != null){
461 return JavaSdk.getInstance().getToolsPath(jdk);
463 return null;
466 @Nullable
467 public String getVMExecutablePath(Sdk sdk) {
468 final Sdk internalJavaSdk = getInternalJavaSdk(sdk);
469 return internalJavaSdk == null ? null : JavaSdk.getInstance().getVMExecutablePath(internalJavaSdk);
472 public void saveAdditionalData(SdkAdditionalData additionalData, Element additional) {
473 if (additionalData instanceof Sandbox) {
474 try {
475 ((Sandbox)additionalData).writeExternal(additional);
477 catch (WriteExternalException e) {
478 LOG.error(e);
483 public SdkAdditionalData loadAdditionalData(Sdk sdk, Element additional) {
484 Sandbox sandbox = new Sandbox(sdk);
485 try {
486 sandbox.readExternal(additional);
488 catch (InvalidDataException e) {
489 LOG.error(e);
491 return sandbox;
494 public String getPresentableName() {
495 return DevKitBundle.message("sdk.title");
498 @Nullable
499 public static Sdk findIdeaJdk(@Nullable Sdk jdk) {
500 if (jdk == null) return null;
501 if (jdk.getSdkType() instanceof IdeaJdk) return jdk;
502 return null;
505 public static SdkType getInstance() {
506 return SdkType.findInstance(IdeaJdk.class);
509 enum JDKVersion {
511 V1_4("1.4"), V1_5("1.5"), V1_6("1.6");
513 private String myPresentation;
515 JDKVersion(String presentation) {
516 myPresentation = presentation;
519 public String getPresentation() {
520 return myPresentation;