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 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
;
44 import java
.io
.FileFilter
;
45 import java
.io
.IOException
;
46 import java
.util
.ArrayList
;
47 import java
.util
.List
;
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";
74 public Icon
getIcon() {
78 public Icon
getIconForExpandedTreeNode() {
82 public Icon
getIconForAddAction() {
86 public String
suggestHomePath() {
87 return PathManager
.getHomePath().replace(File
.separatorChar
, '/');
90 public boolean isValidSdkHome(String path
) {
91 if (isFromIDEAProject(path
)) {
94 File home
= new File(path
);
98 if (getBuildNumber(path
) == null || getOpenApiJar(path
) == null) {
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
;
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
124 return openapiDir
!= null && openapiDir
.length
!= 0;
128 public final String
getVersionString(final Sdk sdk
) {
129 final Sdk internalJavaSdk
= getInternalJavaSdk(sdk
);
130 return internalJavaSdk
!= null ? internalJavaSdk
.getVersionString() : null;
134 private static Sdk
getInternalJavaSdk(final Sdk sdk
) {
135 final SdkAdditionalData data
= sdk
.getSdkAdditionalData();
136 if (data
instanceof Sandbox
) {
137 return ((Sandbox
)data
).getJavaSdk();
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 ";
148 productName
= "IDEA ";
151 String buildNumber
= getBuildNumber(sdkHome
);
152 return productName
+ (buildNumber
!= null ? buildNumber
: "");
156 private static String
getBuildNumber(String ideaHome
) {
158 @NonNls final String buildTxt
= "/build.txt";
159 return new String(FileUtil
.loadFileText(new File(ideaHome
+ buildTxt
))).trim();
161 catch (IOException e
) {
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",
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();
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");
216 Messages
.showErrorDialog(DevKitBundle
.message("no.idea.sdk.version.found"), "No Java SDK found");
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());
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();
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;
255 private static int getIdeaClassFileVersion(final Sdk ideaSdk
) {
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
;
264 ptr
= new BytePointer(mainClassFile
.contentsToByteArray(), 6);
265 result
= ClsUtil
.readU2(ptr
);
267 catch (IOException e
) {
270 catch (ClsFormatException e
) {
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
= "";
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() {
347 sdkModificator
.addRoot(LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(docFile
), JavadocOrderRootType
.getInstance());}
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
) &&
370 Sdk
[] jdks
= ProjectJdkTable
.getInstance().getAllJdks();
371 for (Sdk jdk
: jdks
) {
372 if (jdk
.getSdkType() instanceof JavaSdk
) {
373 addOrderEntries(JavadocOrderRootType
.getInstance(), jdk
, sdkModificator
);
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
);
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
);
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
;
452 public String
getBinPath(Sdk sdk
) {
453 final Sdk internalJavaSdk
= getInternalJavaSdk(sdk
);
454 return internalJavaSdk
== null ?
null : JavaSdk
.getInstance().getBinPath(internalJavaSdk
);
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
);
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
) {
475 ((Sandbox
)additionalData
).writeExternal(additional
);
477 catch (WriteExternalException e
) {
483 public SdkAdditionalData
loadAdditionalData(Sdk sdk
, Element additional
) {
484 Sandbox sandbox
= new Sandbox(sdk
);
486 sandbox
.readExternal(additional
);
488 catch (InvalidDataException e
) {
494 public String
getPresentableName() {
495 return DevKitBundle
.message("sdk.title");
499 public static Sdk
findIdeaJdk(@Nullable Sdk jdk
) {
500 if (jdk
== null) return null;
501 if (jdk
.getSdkType() instanceof IdeaJdk
) return jdk
;
505 public static SdkType
getInstance() {
506 return SdkType
.findInstance(IdeaJdk
.class);
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
;