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.
21 package org
.jetbrains
.idea
.eclipse
.conversion
;
23 import com
.intellij
.openapi
.components
.PathMacroManager
;
24 import com
.intellij
.openapi
.module
.Module
;
25 import com
.intellij
.openapi
.module
.ModuleManager
;
26 import com
.intellij
.openapi
.project
.Project
;
27 import com
.intellij
.openapi
.projectRoots
.ProjectJdkTable
;
28 import com
.intellij
.openapi
.projectRoots
.Sdk
;
29 import com
.intellij
.openapi
.projectRoots
.ex
.JavaSdkUtil
;
30 import com
.intellij
.openapi
.roots
.*;
31 import com
.intellij
.openapi
.roots
.libraries
.Library
;
32 import com
.intellij
.openapi
.roots
.libraries
.LibraryTable
;
33 import com
.intellij
.openapi
.roots
.libraries
.LibraryTablesRegistrar
;
34 import com
.intellij
.openapi
.util
.Comparing
;
35 import com
.intellij
.openapi
.util
.SystemInfo
;
36 import com
.intellij
.openapi
.vfs
.JarFileSystem
;
37 import com
.intellij
.openapi
.vfs
.VfsUtil
;
38 import com
.intellij
.openapi
.vfs
.VirtualFile
;
39 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
40 import com
.intellij
.openapi
.vfs
.ex
.http
.HttpFileSystem
;
41 import com
.intellij
.util
.ArrayUtil
;
42 import org
.jdom
.Element
;
43 import org
.jetbrains
.annotations
.NotNull
;
44 import org
.jetbrains
.annotations
.Nullable
;
45 import org
.jetbrains
.idea
.eclipse
.EclipseXml
;
46 import org
.jetbrains
.idea
.eclipse
.IdeaXml
;
47 import org
.jetbrains
.idea
.eclipse
.config
.EclipseModuleManager
;
48 import org
.jetbrains
.idea
.eclipse
.importWizard
.EclipseProjectFinder
;
49 import org
.jetbrains
.idea
.eclipse
.util
.ErrorLog
;
52 import java
.io
.IOException
;
53 import java
.util
.ArrayList
;
54 import java
.util
.Collection
;
55 import java
.util
.List
;
58 public class EclipseClasspathReader
{
59 private final String myRootPath
;
60 private final Project myProject
;
61 @Nullable private final List
<String
> myCurrentRoots
;
62 private ContentEntry myContentEntry
;
64 public EclipseClasspathReader(final String rootPath
, final Project project
, @Nullable List
<String
> currentRoots
) {
65 myRootPath
= rootPath
;
67 myCurrentRoots
= currentRoots
;
70 public void init(ModifiableRootModel model
) {
71 myContentEntry
= model
.addContentEntry(VfsUtil
.pathToUrl(myRootPath
));
75 public static void collectVariables(Set
<String
> usedVariables
, Element classpathElement
) {
76 for (Object o
: classpathElement
.getChildren(EclipseXml
.CLASSPATHENTRY_TAG
)) {
77 final Element element
= (Element
)o
;
78 final String kind
= element
.getAttributeValue(EclipseXml
.KIND_ATTR
);
79 if (Comparing
.strEqual(kind
, EclipseXml
.VAR_KIND
)) {
80 String path
= element
.getAttributeValue(EclipseXml
.PATH_ATTR
);
81 if (path
== null) continue;
82 int slash
= path
.indexOf("/");
84 usedVariables
.add(path
.substring(0, slash
));
87 usedVariables
.add(path
);
91 final String srcPath
= element
.getAttributeValue(EclipseXml
.SOURCEPATH_ATTR
);
92 if (srcPath
== null) continue;
93 final int varStart
= srcPath
.startsWith("/") ?
1 : 0;
94 final int slash2
= srcPath
.indexOf("/", varStart
);
96 usedVariables
.add(srcPath
.substring(varStart
, slash2
));
99 usedVariables
.add(srcPath
.substring(varStart
));
105 public void readClasspath(ModifiableRootModel model
,
106 final Collection
<String
> unknownLibraries
,
107 Collection
<String
> unknownJdks
,
108 final Set
<String
> usedVariables
,
109 Set
<String
> refsToModules
,
110 final String testPattern
,
111 Element classpathElement
) throws IOException
, ConversionException
{
112 for (OrderEntry orderEntry
: model
.getOrderEntries()) {
113 if (!(orderEntry
instanceof ModuleSourceOrderEntry
)) {
114 model
.removeOrderEntry(orderEntry
);
117 for (Object o
: classpathElement
.getChildren(EclipseXml
.CLASSPATHENTRY_TAG
)) {
119 readClasspathEntry(model
, unknownLibraries
, unknownJdks
, usedVariables
, refsToModules
, testPattern
, (Element
)o
);
121 catch (ConversionException e
) {
122 ErrorLog
.rethrow(ErrorLog
.Level
.Warning
, null, EclipseXml
.CLASSPATH_FILE
, e
);
127 private void readClasspathEntry(ModifiableRootModel rootModel
,
128 final Collection
<String
> unknownLibraries
,
129 Collection
<String
> unknownJdks
,
130 final Set
<String
> usedVariables
,
131 Set
<String
> refsToModules
,
132 final String testPattern
,
133 Element element
) throws ConversionException
{
134 String kind
= element
.getAttributeValue(EclipseXml
.KIND_ATTR
);
136 throw new ConversionException("Missing classpathentry/@kind");
140 String path
= element
.getAttributeValue(EclipseXml
.PATH_ATTR
);
142 throw new ConversionException("Missing classpathentry/@path");
145 final boolean exported
= EclipseXml
.TRUE_VALUE
.equals(element
.getAttributeValue(EclipseXml
.EXPORTED_ATTR
));
147 if (kind
.equals(EclipseXml
.SRC_KIND
)) {
148 if (path
.startsWith("/")) {
149 final String moduleName
= path
.substring(1);
150 refsToModules
.add(moduleName
);
151 rootModel
.addInvalidModuleEntry(moduleName
).setExported(exported
);
154 getContentEntry().addSourceFolder(VfsUtil
.pathToUrl(myRootPath
+ "/" + path
), testPattern
!= null && testPattern
.length() > 0 && path
.matches(testPattern
));
158 else if (kind
.equals(EclipseXml
.OUTPUT_KIND
)) {
159 setupOutput(rootModel
, myRootPath
+ "/" + path
);
162 else if (kind
.equals(EclipseXml
.LIB_KIND
)) {
163 final String libName
= getPresentableName(path
);
164 final Library library
= rootModel
.getModuleLibraryTable().getModifiableModel().createLibrary(libName
);
165 final Library
.ModifiableModel modifiableModel
= library
.getModifiableModel();
167 modifiableModel
.addRoot(getUrl(path
), OrderRootType
.CLASSES
);
169 final String sourcePath
= element
.getAttributeValue(EclipseXml
.SOURCEPATH_ATTR
);
170 if (sourcePath
!= null) {
171 modifiableModel
.addRoot(getUrl(sourcePath
), OrderRootType
.SOURCES
);
174 final List
<String
> docPaths
= getJavadocAttribute(element
);
175 if (docPaths
!= null) {
176 for (String docPath
: docPaths
) {
177 modifiableModel
.addRoot(docPath
, JavadocOrderRootType
.getInstance());
181 modifiableModel
.commit();
183 setLibraryEntryExported(rootModel
, exported
, library
);
185 else if (kind
.equals(EclipseXml
.VAR_KIND
)) {
186 int slash
= path
.indexOf("/");
188 throw new ConversionException("Incorrect 'classpathentry/var@path' format");
191 final String libName
= getPresentableName(path
);
192 final Library library
= rootModel
.getModuleLibraryTable().getModifiableModel().createLibrary(libName
);
193 final Library
.ModifiableModel modifiableModel
= library
.getModifiableModel();
197 final String clsPath
;
199 clsVar
= path
.substring(0, slash
);
200 clsPath
= path
.substring(slash
+ 1);
206 usedVariables
.add(clsVar
);
208 final String url
= getUrl(PathMacroManager
.getInstance(rootModel
.getModule()).expandPath(getVariableRelatedPath(clsVar
, clsPath
)));
209 EclipseModuleManager
.getInstance(rootModel
.getModule()).registerEclipseVariablePath(url
, path
);
210 modifiableModel
.addRoot(url
, OrderRootType
.CLASSES
);
212 final String srcPathAttr
= element
.getAttributeValue(EclipseXml
.SOURCEPATH_ATTR
);
213 if (srcPathAttr
!= null) {
215 final String srcPath
;
216 final int varStart
= srcPathAttr
.startsWith("/") ?
1 : 0;
218 int slash2
= srcPathAttr
.indexOf("/", varStart
);
220 srcVar
= srcPathAttr
.substring(varStart
, slash2
);
221 srcPath
= srcPathAttr
.substring(slash2
+ 1);
224 srcVar
= srcPathAttr
.substring(varStart
);
227 usedVariables
.add(srcVar
);
228 final String srcUrl
= getUrl(PathMacroManager
.getInstance(rootModel
.getModule()).expandPath(getVariableRelatedPath(srcVar
, srcPath
)));
229 EclipseModuleManager
.getInstance(rootModel
.getModule()).registerEclipseSrcVariablePath(srcUrl
, srcPathAttr
);
230 modifiableModel
.addRoot(srcUrl
, OrderRootType
.SOURCES
);
233 final List
<String
> docPaths
= getJavadocAttribute(element
);
234 if (docPaths
!= null) {
235 for (String docPath
: docPaths
) {
236 modifiableModel
.addRoot(docPath
, JavadocOrderRootType
.getInstance());
240 modifiableModel
.commit();
242 setLibraryEntryExported(rootModel
, exported
, library
);
244 else if (kind
.equals(EclipseXml
.CON_KIND
)) {
245 if (path
.equals(EclipseXml
.ECLIPSE_PLATFORM
)) {
246 addNamedLibrary(rootModel
, unknownLibraries
, exported
, IdeaXml
.ECLIPSE_LIBRARY
, LibraryTablesRegistrar
.APPLICATION_LEVEL
);
248 else if (path
.startsWith(EclipseXml
.JRE_CONTAINER
)) {
250 final String jdkName
= getLastPathComponent(path
);
251 if (jdkName
== null) {
252 rootModel
.inheritSdk();
255 final Sdk moduleJdk
= ProjectJdkTable
.getInstance().findJdk(jdkName
);
256 if (moduleJdk
!= null) {
257 rootModel
.setSdk(moduleJdk
);
260 rootModel
.setInvalidSdk(jdkName
, IdeaXml
.JAVA_SDK_TYPE
);
261 unknownJdks
.add(jdkName
);
264 OrderEntry
[] orderEntries
= rootModel
.getOrderEntries();
265 orderEntries
= ArrayUtil
.append(orderEntries
, orderEntries
[0]);
266 rootModel
.rearrangeOrderEntries(ArrayUtil
.remove(orderEntries
, 0));
268 else if (path
.startsWith(EclipseXml
.USER_LIBRARY
)) {
269 addNamedLibrary(rootModel
, unknownLibraries
, exported
, getPresentableName(path
), LibraryTablesRegistrar
.PROJECT_LEVEL
);
271 else if (path
.startsWith(EclipseXml
.JUNIT_CONTAINER
)) {
272 final String junitName
= IdeaXml
.JUNIT
+ getPresentableName(path
);
273 final Library library
= rootModel
.getModuleLibraryTable().getModifiableModel().createLibrary(junitName
);
274 final Library
.ModifiableModel modifiableModel
= library
.getModifiableModel();
275 modifiableModel
.addRoot(getJunitClsUrl(junitName
.contains("4")), OrderRootType
.CLASSES
);
276 modifiableModel
.commit();
280 throw new ConversionException("Unknown classpathentry/@kind: " + kind
);
284 public static void setupOutput(ModifiableRootModel rootModel
, final String path
) {
285 final CompilerModuleExtension compilerModuleExtension
= rootModel
.getModuleExtension(CompilerModuleExtension
.class);
286 compilerModuleExtension
.setCompilerOutputPath(VfsUtil
.pathToUrl(path
));
287 compilerModuleExtension
.inheritCompilerOutputPath(false);
290 private static void setLibraryEntryExported(ModifiableRootModel rootModel
, boolean exported
, Library library
) {
291 for (OrderEntry orderEntry
: rootModel
.getOrderEntries()) {
292 if (orderEntry
instanceof LibraryOrderEntry
&&
293 ((LibraryOrderEntry
)orderEntry
).isModuleLevel() &&
294 Comparing
.equal(((LibraryOrderEntry
)orderEntry
).getLibrary(), library
)) {
295 ((LibraryOrderEntry
)orderEntry
).setExported(exported
);
301 private void addNamedLibrary(final ModifiableRootModel rootModel
,
302 final Collection
<String
> unknownLibraries
,
303 final boolean exported
,
305 final String notFoundLibraryLevel
) {
306 Library lib
= findLibraryByName(myProject
, name
);
308 rootModel
.addLibraryEntry(lib
).setExported(exported
);
311 unknownLibraries
.add(name
);
312 rootModel
.addInvalidLibrary(name
, notFoundLibraryLevel
).setExported(exported
);
316 public static Library
findLibraryByName(Project project
, String name
) {
317 final LibraryTablesRegistrar tablesRegistrar
= LibraryTablesRegistrar
.getInstance();
318 Library lib
= tablesRegistrar
.getLibraryTable().getLibraryByName(name
);
320 lib
= tablesRegistrar
.getLibraryTable(project
).getLibraryByName(name
);
323 for (LibraryTable table
: tablesRegistrar
.getCustomLibraryTables()) {
324 lib
= table
.getLibraryByName(name
);
334 private static String
getPresentableName(@NotNull String path
) {
335 final String pathComponent
= getLastPathComponent(path
);
336 return pathComponent
!= null ? pathComponent
: path
;
340 public static String
getLastPathComponent(final String path
) {
341 final int idx
= path
.lastIndexOf('/');
342 return idx
< 0 || idx
== path
.length() - 1 ?
null : path
.substring(idx
+ 1);
345 private ContentEntry
getContentEntry() {
346 return myContentEntry
;
349 private static String
getVariableRelatedPath(String var
, String path
) {
350 return var
== null ?
null : ("$" + var
+ "$" + (path
== null ?
"" : ("/" + path
)));
353 private String
getUrl(final String path
) {
355 if (path
.startsWith("/")) {
356 final String relativePath
= new File(myRootPath
).getParent() + "/" + path
;
357 final File file
= new File(relativePath
);
359 url
= VfsUtil
.pathToUrl(relativePath
);
360 } else if (new File(path
).exists()) {
361 url
= VfsUtil
.pathToUrl(path
);
364 final String rootPath
= getRootPath(path
);
365 final String relativeToRootPath
= getRelativeToRootPath(path
);
367 final Module otherModule
= ModuleManager
.getInstance(myProject
).findModuleByName(rootPath
);
368 if (otherModule
!= null) {
369 url
= relativeToOtherModule(otherModule
, relativeToRootPath
);
371 else if (myCurrentRoots
!= null) {
372 url
= relativeToContentRoots(myCurrentRoots
, rootPath
, relativeToRootPath
);
377 final String absPath
= myRootPath
+ "/" + path
;
378 if (new File(absPath
).exists()) {
379 url
= VfsUtil
.pathToUrl(absPath
);
382 url
= VfsUtil
.pathToUrl(path
);
385 final VirtualFile localFile
= VirtualFileManager
.getInstance().findFileByUrl(url
);
386 if (localFile
!= null) {
387 final VirtualFile jarFile
= JarFileSystem
.getInstance().getJarRootForLocalFile(localFile
);
388 if (jarFile
!= null) {
389 url
= jarFile
.getUrl();
396 * @param path path in format /module_root/relative_path
397 * @return module_root
400 private static String
getRootPath(String path
) {
401 int secondSlIdx
= path
.indexOf('/', 1);
402 return secondSlIdx
> 1 ? path
.substring(1, secondSlIdx
) : path
.substring(1);
406 * @param path path in format /module_root/relative_path
407 * @return relative_path or null if /module_root
410 private static String
getRelativeToRootPath(String path
) {
411 final int secondSlIdx
= path
.indexOf('/', 1);
412 return secondSlIdx
!= -1 && secondSlIdx
+ 1 < path
.length() ? path
.substring(secondSlIdx
+ 1) : null;
416 private static String
relativeToContentRoots(final @NotNull List
<String
> currentRoots
,
417 final @NotNull String rootPath
,
418 final @Nullable String relativeToRootPath
) {
419 for (String currentRoot
: currentRoots
) {
420 if (currentRoot
.endsWith(rootPath
) || Comparing
.strEqual(rootPath
, EclipseProjectFinder
.findProjectName(currentRoot
))) { //rootPath = content_root <=> applicable root: abs_path/content_root
421 if (relativeToRootPath
== null) {
422 return VfsUtil
.pathToUrl(currentRoot
);
424 final File relativeToOtherModuleFile
= new File(currentRoot
, relativeToRootPath
);
425 if (relativeToOtherModuleFile
.exists()) {
426 return VfsUtil
.pathToUrl(relativeToOtherModuleFile
.getPath());
434 private static String
relativeToOtherModule(final @NotNull Module otherModule
, final @Nullable String relativeToOtherModule
) {
435 final VirtualFile
[] contentRoots
= ModuleRootManager
.getInstance(otherModule
).getContentRoots();
436 for (VirtualFile contentRoot
: contentRoots
) {
437 if (relativeToOtherModule
== null) {
438 return contentRoot
.getUrl();
440 final File relativeToOtherModuleFile
= new File(contentRoot
.getPath(), relativeToOtherModule
);
441 if (relativeToOtherModuleFile
.exists()) {
442 return VfsUtil
.pathToUrl(relativeToOtherModuleFile
.getPath());
449 private List
<String
> getJavadocAttribute(Element element
) {
450 Element attributes
= element
.getChild("attributes");
451 if (attributes
== null) {
454 List
<String
> result
= new ArrayList
<String
>();
455 for (Object o
: attributes
.getChildren("attribute")) {
456 if (Comparing
.strEqual(((Element
)o
).getAttributeValue("name"), "javadoc_location")) {
457 Element attribute
= (Element
)o
;
458 String javadocPath
= attribute
.getAttributeValue("value");
459 if (!SystemInfo
.isWindows
) {
460 javadocPath
= javadocPath
.replaceFirst(EclipseXml
.FILE_PROTOCOL
, EclipseXml
.FILE_PROTOCOL
+ "/");
462 if (javadocPath
.startsWith(EclipseXml
.FILE_PROTOCOL
) &&
463 new File(javadocPath
.substring(EclipseXml
.FILE_PROTOCOL
.length())).exists()) {
464 result
.add(VfsUtil
.pathToUrl(javadocPath
.substring(EclipseXml
.FILE_PROTOCOL
.length())));
468 final String protocol
= VirtualFileManager
.extractProtocol(javadocPath
);
469 if (Comparing
.strEqual(protocol
, HttpFileSystem
.getInstance().getProtocol())) {
470 result
.add(javadocPath
);
472 else if (javadocPath
.startsWith(EclipseXml
.JAR_PREFIX
)) {
473 final String jarJavadocPath
= javadocPath
.substring(EclipseXml
.JAR_PREFIX
.length());
474 if (jarJavadocPath
.startsWith(EclipseXml
.PLATFORM_PROTOCOL
)) {
475 String relativeToPlatform
= jarJavadocPath
.substring(EclipseXml
.PLATFORM_PROTOCOL
.length() + "resources".length());
477 .add(VirtualFileManager
.constructUrl(JarFileSystem
.PROTOCOL
, new File(myRootPath
).getParent() + "/" + relativeToPlatform
));
479 else if (jarJavadocPath
.startsWith(EclipseXml
.FILE_PROTOCOL
)) {
481 .add(VirtualFileManager
.constructUrl(JarFileSystem
.PROTOCOL
, jarJavadocPath
.substring(EclipseXml
.FILE_PROTOCOL
.length())));
484 result
.add(javadocPath
);
493 static String
getJunitClsUrl(final boolean version4
) {
494 String url
= version4 ? JavaSdkUtil
.getJunit4JarPath() : JavaSdkUtil
.getJunit3JarPath();
495 final VirtualFile localFile
= VirtualFileManager
.getInstance().findFileByUrl(VfsUtil
.pathToUrl(url
));
496 if (localFile
!= null) {
497 final VirtualFile jarFile
= JarFileSystem
.getInstance().getJarRootForLocalFile(localFile
);
498 url
= jarFile
!= null ? jarFile
.getUrl() : localFile
.getUrl();