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
.openapi
.projectRoots
.impl
;
18 import com
.intellij
.openapi
.application
.PathManager
;
19 import com
.intellij
.openapi
.project
.ProjectBundle
;
20 import com
.intellij
.openapi
.projectRoots
.*;
21 import com
.intellij
.openapi
.roots
.JavadocOrderRootType
;
22 import com
.intellij
.openapi
.roots
.OrderRootType
;
23 import com
.intellij
.openapi
.util
.IconLoader
;
24 import com
.intellij
.openapi
.util
.SystemInfo
;
25 import com
.intellij
.openapi
.vfs
.JarFileSystem
;
26 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
27 import com
.intellij
.openapi
.vfs
.VirtualFile
;
28 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
29 import com
.intellij
.util
.containers
.HashMap
;
30 import org
.jdom
.Element
;
31 import org
.jetbrains
.annotations
.NonNls
;
32 import org
.jetbrains
.annotations
.NotNull
;
33 import org
.jetbrains
.annotations
.Nullable
;
37 import java
.io
.FileFilter
;
39 import java
.util
.regex
.Matcher
;
40 import java
.util
.regex
.Pattern
;
43 * @author Eugene Zhuravlev
46 public class JavaSdkImpl
extends JavaSdk
{
47 // do not use javaw.exe for Windows because of issues with encoding
48 @NonNls private static final String VM_EXE_NAME
= "java";
49 @NonNls private final Pattern myVersionStringPattern
= Pattern
.compile("^(.*)java version \"([1234567890_.]*)\"(.*)$");
50 public static final Icon ICON
= IconLoader
.getIcon("/nodes/ppJdkClosed.png");
51 private static final Icon JDK_ICON_EXPANDED
= IconLoader
.getIcon("/nodes/ppJdkOpen.png");
52 private static final Icon ADD_ICON
= IconLoader
.getIcon("/general/addJdk.png");
53 @NonNls private static final String JAVA_VERSION_PREFIX
= "java version ";
55 public JavaSdkImpl() {
59 public String
getPresentableName() {
60 return ProjectBundle
.message("sdk.java.name");
63 public Icon
getIcon() {
67 public Icon
getIconForExpandedTreeNode() {
68 return JDK_ICON_EXPANDED
;
71 public Icon
getIconForAddAction() {
75 public AdditionalDataConfigurable
createAdditionalDataConfigurable(SdkModel sdkModel
, SdkModificator sdkModificator
) {
79 public void saveAdditionalData(SdkAdditionalData additionalData
, Element additional
) {
82 public SdkAdditionalData
loadAdditionalData(Element additional
) {
86 @SuppressWarnings({"HardCodedStringLiteral"})
87 public String
getBinPath(Sdk sdk
) {
88 return getConvertedHomePath(sdk
) + "bin";
92 public String
getToolsPath(Sdk sdk
) {
93 final String versionString
= sdk
.getVersionString();
94 final boolean isJdk1_x
= versionString
!= null && (versionString
.contains("1.0") || versionString
.contains("1.1"));
95 return getConvertedHomePath(sdk
) + "lib" + File
.separator
+ (isJdk1_x?
"classes.zip" : "tools.jar");
98 public String
getVMExecutablePath(Sdk sdk
) {
100 if ("64".equals(System.getProperty("sun.arch.data.model"))) {
101 return getBinPath(sdk) + File.separator + System.getProperty("os.arch") + File.separator + VM_EXE_NAME;
104 return getBinPath(sdk
) + File
.separator
+ VM_EXE_NAME
;
107 private static String
getConvertedHomePath(Sdk sdk
) {
108 String path
= sdk
.getHomePath().replace('/', File
.separatorChar
);
109 if (!path
.endsWith(File
.separator
)) {
110 path
+= File
.separator
;
115 @SuppressWarnings({"HardCodedStringLiteral"})
116 public String
suggestHomePath() {
117 if (SystemInfo
.isMac
) {
118 return "/System/Library/Frameworks/JavaVM.framework/Versions/";
123 public boolean isValidSdkHome(String path
) {
124 return checkForJdk(new File(path
));
127 public String
suggestSdkName(String currentSdkName
, String sdkHome
) {
128 final String suggestedName
;
129 if (currentSdkName
!= null && currentSdkName
.length() > 0) {
130 final Matcher matcher
= myVersionStringPattern
.matcher(currentSdkName
);
131 final boolean replaceNameWithVersion
= matcher
.matches();
132 if (replaceNameWithVersion
){
133 // user did not change name -> set it automatically
134 final String versionString
= getVersionString(sdkHome
);
135 suggestedName
= versionString
== null ? currentSdkName
: matcher
.replaceFirst("$1" + versionString
+ "$3");
138 suggestedName
= currentSdkName
;
142 String versionString
= getVersionString(sdkHome
);
143 if (versionString
!= null) {
144 suggestedName
= getVersionNumber(versionString
);
146 suggestedName
= ProjectBundle
.message("sdk.java.unknown.name");
149 return suggestedName
;
152 private static String
getVersionNumber(String versionString
) {
153 if (versionString
.startsWith(JAVA_VERSION_PREFIX
)) {
154 versionString
= versionString
.substring(JAVA_VERSION_PREFIX
.length());
155 if (versionString
.startsWith("\"") && versionString
.endsWith("\"")) {
156 versionString
= versionString
.substring(1, versionString
.length() - 1);
158 int dotIdx
= versionString
.indexOf('.');
161 int major
= Integer
.parseInt(versionString
.substring(0, dotIdx
));
162 int minorDot
= versionString
.indexOf('.', dotIdx
+ 1);
164 int minor
= Integer
.parseInt(versionString
.substring(dotIdx
+ 1, minorDot
));
165 versionString
= String
.valueOf(major
) + "." + String
.valueOf(minor
);
168 catch (NumberFormatException e
) {
169 // Do nothing. Use original version string if failed to parse according to major.minor pattern.
173 return versionString
;
176 @SuppressWarnings({"HardCodedStringLiteral"})
177 public void setupSdkPaths(Sdk sdk
) {
178 final File jdkHome
= new File(sdk
.getHomePath());
179 VirtualFile
[] classes
= findClasses(jdkHome
, false);
180 VirtualFile sources
= findSources(jdkHome
);
181 VirtualFile docs
= findDocs(jdkHome
, "docs/api");
183 final SdkModificator sdkModificator
= sdk
.getSdkModificator();
184 final Set
<VirtualFile
> previousRoots
= new LinkedHashSet
<VirtualFile
>(Arrays
.asList(sdkModificator
.getRoots(OrderRootType
.CLASSES
)));
185 sdkModificator
.removeRoots(OrderRootType
.CLASSES
);
186 previousRoots
.removeAll(new HashSet
<VirtualFile
>(Arrays
.asList(classes
)));
187 for (VirtualFile aClass
: classes
) {
188 sdkModificator
.addRoot(aClass
, OrderRootType
.CLASSES
);
190 for (VirtualFile root
: previousRoots
) {
191 sdkModificator
.addRoot(root
, OrderRootType
.CLASSES
);
194 sdkModificator
.addRoot(sources
, OrderRootType
.SOURCES
);
197 sdkModificator
.addRoot(docs
, JavadocOrderRootType
.getInstance());
199 else if (SystemInfo
.isMac
) {
200 VirtualFile commonDocs
= findDocs(jdkHome
, "docs");
201 if (commonDocs
== null) {
202 commonDocs
= findInJar(new File(jdkHome
, "docs.jar"), "doc/api");
204 if (commonDocs
!= null) {
205 sdkModificator
.addRoot(commonDocs
, JavadocOrderRootType
.getInstance());
208 VirtualFile appleDocs
= findDocs(jdkHome
, "appledocs");
209 if (appleDocs
== null) {
210 appleDocs
= findInJar(new File(jdkHome
, "appledocs.jar"), "appledoc/api");
212 if (appleDocs
!= null) {
213 sdkModificator
.addRoot(appleDocs
, JavadocOrderRootType
.getInstance());
216 sdkModificator
.commitChanges();
219 private final Map
<String
, String
> myCachedVersionStrings
= new HashMap
<String
, String
>();
221 public final String
getVersionString(final String sdkHome
) {
222 String versionString
;
224 if(myCachedVersionStrings
.containsKey(sdkHome
)) {
225 return myCachedVersionStrings
.get(sdkHome
);
227 versionString
= getJdkVersion(sdkHome
);
229 if (versionString
!= null && versionString
.length() == 0) {
230 versionString
= null;
233 if (versionString
!= null){
234 myCachedVersionStrings
.put(sdkHome
, versionString
);
237 return versionString
;
241 public String
getComponentName() {
245 public void initComponent() { }
247 public void disposeComponent() {
250 public int compareTo(@NotNull String versionString
, @NotNull String versionNumber
) {
251 return getVersionNumber(versionString
).compareTo(versionNumber
);
254 public Sdk
createJdk(final String jdkName
, final String home
, final boolean isJre
) {
255 ProjectJdkImpl jdk
= new ProjectJdkImpl(jdkName
, this);
256 SdkModificator sdkModificator
= jdk
.getSdkModificator();
258 String path
= home
.replace(File
.separatorChar
, '/');
259 sdkModificator
.setHomePath(path
);
260 jdk
.setVersionString(jdkName
); // must be set after home path, otherwise setting home path clears the version string
262 File jdkHomeFile
= new File(home
);
263 addClasses(jdkHomeFile
, sdkModificator
, isJre
);
264 addSources(jdkHomeFile
, sdkModificator
);
265 addDocs(jdkHomeFile
, sdkModificator
);
266 sdkModificator
.commitChanges();
270 public static Sdk
getMockJdk(@NonNls String versionName
) {
271 File mockJdkCEPath
= new File(PathManager
.getHomePath(), "java/mockJDK");
272 if (mockJdkCEPath
.exists()) {
273 return createMockJdk(mockJdkCEPath
.getPath(), versionName
, getInstance());
275 final String forcedPath
= System
.getProperty("idea.testingFramework.mockJDK");
276 String jdkHome
= forcedPath
!= null ? forcedPath
: PathManager
.getHomePath() + File
.separator
+ "mockJDK";
277 return createMockJdk(jdkHome
, versionName
, getInstance());
280 public static Sdk
getMockJdk15(@NonNls String versionName
) {
281 File mockJdkCEPath
= new File(PathManager
.getHomePath(), "java/mockJDK");
282 if (mockJdkCEPath
.exists()) {
283 return createMockJdk(mockJdkCEPath
.getPath(), versionName
, getInstance());
285 String jdkHome
= PathManager
.getHomePath() + File
.separator
+ "mockJDK-1.5";
286 return createMockJdk(jdkHome
, versionName
, getInstance());
289 public static Sdk
getMockJdk17(@NonNls String versionName
) {
290 String jdkHome
= PathManager
.getHomePath() + File
.separator
+ "mockJDK-1.7";
291 return createMockJdk(jdkHome
, versionName
, getInstance());
294 private static Sdk
createMockJdk(String jdkHome
, final String versionName
, JavaSdk javaSdk
) {
295 File jdkHomeFile
= new File(jdkHome
);
296 if (!jdkHomeFile
.exists()) return null;
298 final Sdk jdk
= new ProjectJdkImpl(versionName
, javaSdk
);
299 final SdkModificator sdkModificator
= jdk
.getSdkModificator();
301 String path
= jdkHome
.replace(File
.separatorChar
, '/');
302 sdkModificator
.setHomePath(path
);
303 sdkModificator
.setVersionString(versionName
); // must be set after home path, otherwise setting home path clears the version string
305 addSources(jdkHomeFile
, sdkModificator
);
306 addClasses(jdkHomeFile
, sdkModificator
, false);
307 addClasses(jdkHomeFile
, sdkModificator
, true);
308 sdkModificator
.commitChanges();
313 private static void addClasses(File file
, SdkModificator sdkModificator
, final boolean isJre
) {
314 VirtualFile
[] classes
= findClasses(file
, isJre
);
315 for (VirtualFile virtualFile
: classes
) {
316 sdkModificator
.addRoot(virtualFile
, OrderRootType
.CLASSES
);
320 private static VirtualFile
[] findClasses(File file
, boolean isJre
) {
321 FileFilter jarFileFilter
= new FileFilter(){
322 @SuppressWarnings({"HardCodedStringLiteral"})
323 public boolean accept(File f
){
324 if (f
.isDirectory()) return false;
325 if (f
.getName().endsWith(".jar")) return true;
331 if(SystemInfo
.isMac
&& /*!ApplicationManager.getApplication().isUnitTestMode()) &&*/ !file
.getName().startsWith("mockJDK")){
332 File libFile
= new File(file
, "lib");
333 @NonNls File classesFile
= new File(file
, "../Classes");
334 @NonNls File libExtFile
= new File(libFile
, "ext");
335 @NonNls File libEndorsedFile
= new File(libFile
, "endorsed");
336 jarDirs
= new File
[]{libEndorsedFile
, libFile
, classesFile
, libExtFile
};
339 @NonNls final String jre
= "jre";
340 File jreLibFile
= isJre ?
new File(file
, "lib") : new File(new File(file
, jre
), "lib");
341 @NonNls File jreLibExtFile
= new File(jreLibFile
, "ext");
342 @NonNls File jreLibEndorsedFile
= new File(jreLibFile
, "endorsed");
343 jarDirs
= new File
[]{jreLibEndorsedFile
, jreLibFile
, jreLibExtFile
};
346 ArrayList
<File
> childrenList
= new ArrayList
<File
>();
347 for (File jarDir
: jarDirs
) {
348 if (jarDir
!= null && jarDir
.isDirectory()) {
349 File
[] files
= jarDir
.listFiles(jarFileFilter
);
350 for (File file1
: files
) {
351 childrenList
.add(file1
);
356 ArrayList
<VirtualFile
> result
= new ArrayList
<VirtualFile
>();
357 for (File child
: childrenList
) {
358 String url
= JarFileSystem
.PROTOCOL_PREFIX
+ child
.getAbsolutePath().replace(File
.separatorChar
, '/') + JarFileSystem
.JAR_SEPARATOR
;
359 VirtualFile vFile
= VirtualFileManager
.getInstance().findFileByUrl(url
);
365 @NonNls File classesZipFile
= new File(new File(file
, "lib"), "classes.zip");
366 if(!classesZipFile
.isDirectory() && classesZipFile
.exists()){
368 JarFileSystem
.PROTOCOL_PREFIX
+ classesZipFile
.getAbsolutePath().replace(File
.separatorChar
, '/') + JarFileSystem
.JAR_SEPARATOR
;
369 VirtualFile vFile
= VirtualFileManager
.getInstance().findFileByUrl(url
);
375 return result
.toArray(new VirtualFile
[result
.size()]);
378 private static void addSources(File file
, SdkModificator sdkModificator
) {
379 VirtualFile vFile
= findSources(file
);
381 sdkModificator
.addRoot(vFile
, OrderRootType
.SOURCES
);
386 @SuppressWarnings({"HardCodedStringLiteral"})
387 public static VirtualFile
findSources(File file
) {
388 File srcfile
= new File(file
, "src");
389 File jarfile
= new File(file
, "src.jar");
390 if (!jarfile
.exists()) {
391 jarfile
= new File(file
, "src.zip");
394 if (jarfile
.exists()) {
395 VirtualFile vFile
= findInJar(jarfile
, "src");
396 if (vFile
!= null) return vFile
;
398 vFile
= findInJar(jarfile
, "");
402 if (!srcfile
.exists() || !srcfile
.isDirectory()) return null;
403 String path
= srcfile
.getAbsolutePath().replace(File
.separatorChar
, '/');
404 return LocalFileSystem
.getInstance().findFileByPath(path
);
408 @SuppressWarnings({"HardCodedStringLiteral"})
409 private static void addDocs(File file
, SdkModificator rootContainer
) {
410 VirtualFile vFile
= findDocs(file
, "docs/api");
412 rootContainer
.addRoot(vFile
, JavadocOrderRootType
.getInstance());
417 private static VirtualFile
findInJar(File jarFile
, String relativePath
) {
418 if (!jarFile
.exists()) return null;
419 String url
= JarFileSystem
.PROTOCOL_PREFIX
+
420 jarFile
.getAbsolutePath().replace(File
.separatorChar
, '/') + JarFileSystem
.JAR_SEPARATOR
+ relativePath
;
421 return VirtualFileManager
.getInstance().findFileByUrl(url
);
425 public static VirtualFile
findDocs(File file
, final String relativePath
) {
426 file
= new File(file
.getAbsolutePath() + File
.separator
+ relativePath
.replace('/', File
.separatorChar
));
427 if (!file
.exists() || !file
.isDirectory()) return null;
428 String path
= file
.getAbsolutePath().replace(File
.separatorChar
, '/');
429 return LocalFileSystem
.getInstance().findFileByPath(path
);