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
.util
.ErrorLog
;
51 import java
.io
.IOException
;
54 public class EclipseClasspathReader
{
55 private final String myRootPath
;
56 private final Project myProject
;
57 @Nullable private final List
<String
> myCurrentRoots
;
58 private ContentEntry myContentEntry
;
60 public EclipseClasspathReader(final String rootPath
, final Project project
, @Nullable List
<String
> currentRoots
) {
61 myRootPath
= rootPath
;
63 myCurrentRoots
= currentRoots
;
66 public void init(ModifiableRootModel model
) {
67 myContentEntry
= model
.addContentEntry(VfsUtil
.pathToUrl(myRootPath
));
71 public static void collectVariables(Set
<String
> usedVariables
, Element classpathElement
) {
72 for (Object o
: classpathElement
.getChildren(EclipseXml
.CLASSPATHENTRY_TAG
)) {
73 final Element element
= (Element
)o
;
74 final String kind
= element
.getAttributeValue(EclipseXml
.KIND_ATTR
);
75 if (Comparing
.strEqual(kind
, EclipseXml
.VAR_KIND
)) {
76 String path
= element
.getAttributeValue(EclipseXml
.PATH_ATTR
);
77 if (path
== null) continue;
78 int slash
= path
.indexOf("/");
80 usedVariables
.add(path
.substring(0, slash
));
83 usedVariables
.add(path
);
87 final String srcPath
= element
.getAttributeValue(EclipseXml
.SOURCEPATH_ATTR
);
88 if (srcPath
== null) continue;
89 final int varStart
= srcPath
.startsWith("/") ?
1 : 0;
90 final int slash2
= srcPath
.indexOf("/", varStart
);
92 usedVariables
.add(srcPath
.substring(varStart
, slash2
));
95 usedVariables
.add(srcPath
.substring(varStart
));
101 public void readClasspath(ModifiableRootModel model
,
102 final Collection
<String
> unknownLibraries
,
103 Collection
<String
> unknownJdks
,
104 final Set
<String
> usedVariables
,
105 Set
<String
> refsToModules
,
106 final String testPattern
,
107 Element classpathElement
) throws IOException
, ConversionException
{
108 for (OrderEntry orderEntry
: model
.getOrderEntries()) {
109 if (!(orderEntry
instanceof ModuleSourceOrderEntry
)) {
110 model
.removeOrderEntry(orderEntry
);
113 for (Object o
: classpathElement
.getChildren(EclipseXml
.CLASSPATHENTRY_TAG
)) {
115 readClasspathEntry(model
, unknownLibraries
, unknownJdks
, usedVariables
, refsToModules
, testPattern
, (Element
)o
);
117 catch (ConversionException e
) {
118 ErrorLog
.rethrow(ErrorLog
.Level
.Warning
, null, EclipseXml
.CLASSPATH_FILE
, e
);
123 private void readClasspathEntry(ModifiableRootModel rootModel
,
124 final Collection
<String
> unknownLibraries
,
125 Collection
<String
> unknownJdks
,
126 final Set
<String
> usedVariables
,
127 Set
<String
> refsToModules
,
128 final String testPattern
,
129 Element element
) throws ConversionException
{
130 String kind
= element
.getAttributeValue(EclipseXml
.KIND_ATTR
);
132 throw new ConversionException("Missing classpathentry/@kind");
136 String path
= element
.getAttributeValue(EclipseXml
.PATH_ATTR
);
138 throw new ConversionException("Missing classpathentry/@path");
141 final boolean exported
= EclipseXml
.TRUE_VALUE
.equals(element
.getAttributeValue(EclipseXml
.EXPORTED_ATTR
));
143 if (kind
.equals(EclipseXml
.SRC_KIND
)) {
144 if (path
.startsWith("/")) {
145 final String moduleName
= path
.substring(1);
146 refsToModules
.add(moduleName
);
147 rootModel
.addInvalidModuleEntry(moduleName
).setExported(exported
);
150 getContentEntry().addSourceFolder(VfsUtil
.pathToUrl(myRootPath
+ "/" + path
), testPattern
!= null && testPattern
.length() > 0 && path
.matches(testPattern
));
154 else if (kind
.equals(EclipseXml
.OUTPUT_KIND
)) {
155 setupOutput(rootModel
, myRootPath
+ "/" + path
);
158 else if (kind
.equals(EclipseXml
.LIB_KIND
)) {
159 final String libName
= getPresentableName(path
);
160 final Library library
= rootModel
.getModuleLibraryTable().getModifiableModel().createLibrary(libName
);
161 final Library
.ModifiableModel modifiableModel
= library
.getModifiableModel();
163 modifiableModel
.addRoot(getUrl(path
), OrderRootType
.CLASSES
);
165 final String sourcePath
= element
.getAttributeValue(EclipseXml
.SOURCEPATH_ATTR
);
166 if (sourcePath
!= null) {
167 modifiableModel
.addRoot(getUrl(sourcePath
), OrderRootType
.SOURCES
);
170 final List
<String
> docPaths
= getJavadocAttribute(element
);
171 if (docPaths
!= null) {
172 for (String docPath
: docPaths
) {
173 modifiableModel
.addRoot(docPath
, JavadocOrderRootType
.getInstance());
177 modifiableModel
.commit();
179 setLibraryEntryExported(rootModel
, exported
, library
);
181 else if (kind
.equals(EclipseXml
.VAR_KIND
)) {
182 int slash
= path
.indexOf("/");
184 throw new ConversionException("Incorrect 'classpathentry/var@path' format");
187 final String libName
= getPresentableName(path
);
188 final Library library
= rootModel
.getModuleLibraryTable().getModifiableModel().createLibrary(libName
);
189 final Library
.ModifiableModel modifiableModel
= library
.getModifiableModel();
193 final String clsPath
;
195 clsVar
= path
.substring(0, slash
);
196 clsPath
= path
.substring(slash
+ 1);
202 usedVariables
.add(clsVar
);
204 final String url
= getUrl(PathMacroManager
.getInstance(rootModel
.getModule()).expandPath(getVariableRelatedPath(clsVar
, clsPath
)));
205 EclipseModuleManager
.getInstance(rootModel
.getModule()).registerEclipseVariablePath(url
, path
);
206 modifiableModel
.addRoot(url
, OrderRootType
.CLASSES
);
208 final String srcPathAttr
= element
.getAttributeValue(EclipseXml
.SOURCEPATH_ATTR
);
209 if (srcPathAttr
!= null) {
211 final String srcPath
;
212 final int varStart
= srcPathAttr
.startsWith("/") ?
1 : 0;
214 int slash2
= srcPathAttr
.indexOf("/", varStart
);
216 srcVar
= srcPathAttr
.substring(varStart
, slash2
);
217 srcPath
= srcPathAttr
.substring(slash2
+ 1);
220 srcVar
= srcPathAttr
.substring(varStart
);
223 usedVariables
.add(srcVar
);
224 final String srcUrl
= getUrl(PathMacroManager
.getInstance(rootModel
.getModule()).expandPath(getVariableRelatedPath(srcVar
, srcPath
)));
225 EclipseModuleManager
.getInstance(rootModel
.getModule()).registerEclipseSrcVariablePath(srcUrl
, srcPathAttr
);
226 modifiableModel
.addRoot(srcUrl
, OrderRootType
.SOURCES
);
229 final List
<String
> docPaths
= getJavadocAttribute(element
);
230 if (docPaths
!= null) {
231 for (String docPath
: docPaths
) {
232 modifiableModel
.addRoot(docPath
, JavadocOrderRootType
.getInstance());
236 modifiableModel
.commit();
238 setLibraryEntryExported(rootModel
, exported
, library
);
240 else if (kind
.equals(EclipseXml
.CON_KIND
)) {
241 if (path
.equals(EclipseXml
.ECLIPSE_PLATFORM
)) {
242 addNamedLibrary(rootModel
, unknownLibraries
, exported
, IdeaXml
.ECLIPSE_LIBRARY
, LibraryTablesRegistrar
.APPLICATION_LEVEL
);
244 else if (path
.startsWith(EclipseXml
.JRE_CONTAINER
)) {
246 final String jdkName
= getLastPathComponent(path
);
247 if (jdkName
== null) {
248 rootModel
.inheritSdk();
251 final Sdk moduleJdk
= ProjectJdkTable
.getInstance().findJdk(jdkName
);
252 if (moduleJdk
!= null) {
253 rootModel
.setSdk(moduleJdk
);
256 rootModel
.setInvalidSdk(jdkName
, IdeaXml
.JAVA_SDK_TYPE
);
257 unknownJdks
.add(jdkName
);
260 OrderEntry
[] orderEntries
= rootModel
.getOrderEntries();
261 orderEntries
= ArrayUtil
.append(orderEntries
, orderEntries
[0]);
262 rootModel
.rearrangeOrderEntries(ArrayUtil
.remove(orderEntries
, 0));
264 else if (path
.startsWith(EclipseXml
.USER_LIBRARY
)) {
265 addNamedLibrary(rootModel
, unknownLibraries
, exported
, getPresentableName(path
), LibraryTablesRegistrar
.PROJECT_LEVEL
);
267 else if (path
.startsWith(EclipseXml
.JUNIT_CONTAINER
)) {
268 final String junitName
= IdeaXml
.JUNIT
+ getPresentableName(path
);
269 final Library library
= rootModel
.getModuleLibraryTable().getModifiableModel().createLibrary(junitName
);
270 final Library
.ModifiableModel modifiableModel
= library
.getModifiableModel();
271 modifiableModel
.addRoot(getJunitClsUrl(junitName
.contains("4")), OrderRootType
.CLASSES
);
272 modifiableModel
.commit();
276 throw new ConversionException("Unknown classpathentry/@kind: " + kind
);
280 public static void setupOutput(ModifiableRootModel rootModel
, final String path
) {
281 final CompilerModuleExtension compilerModuleExtension
= rootModel
.getModuleExtension(CompilerModuleExtension
.class);
282 compilerModuleExtension
.setCompilerOutputPath(VfsUtil
.pathToUrl(path
));
283 compilerModuleExtension
.inheritCompilerOutputPath(false);
286 private static void setLibraryEntryExported(ModifiableRootModel rootModel
, boolean exported
, Library library
) {
287 for (OrderEntry orderEntry
: rootModel
.getOrderEntries()) {
288 if (orderEntry
instanceof LibraryOrderEntry
&&
289 ((LibraryOrderEntry
)orderEntry
).isModuleLevel() &&
290 Comparing
.equal(((LibraryOrderEntry
)orderEntry
).getLibrary(), library
)) {
291 ((LibraryOrderEntry
)orderEntry
).setExported(exported
);
297 private void addNamedLibrary(final ModifiableRootModel rootModel
,
298 final Collection
<String
> unknownLibraries
,
299 final boolean exported
,
301 final String notFoundLibraryLevel
) {
302 Library lib
= findLibraryByName(myProject
, name
);
304 rootModel
.addLibraryEntry(lib
).setExported(exported
);
307 unknownLibraries
.add(name
);
308 rootModel
.addInvalidLibrary(name
, notFoundLibraryLevel
).setExported(exported
);
312 public static Library
findLibraryByName(Project project
, String name
) {
313 final LibraryTablesRegistrar tablesRegistrar
= LibraryTablesRegistrar
.getInstance();
314 Library lib
= tablesRegistrar
.getLibraryTable().getLibraryByName(name
);
316 lib
= tablesRegistrar
.getLibraryTable(project
).getLibraryByName(name
);
319 for (LibraryTable table
: tablesRegistrar
.getCustomLibraryTables()) {
320 lib
= table
.getLibraryByName(name
);
330 private static String
getPresentableName(@NotNull String path
) {
331 final String pathComponent
= getLastPathComponent(path
);
332 return pathComponent
!= null ? pathComponent
: path
;
336 public static String
getLastPathComponent(final String path
) {
337 final int idx
= path
.lastIndexOf('/');
338 return idx
< 0 || idx
== path
.length() - 1 ?
null : path
.substring(idx
+ 1);
341 private ContentEntry
getContentEntry() {
342 return myContentEntry
;
345 private static String
getVariableRelatedPath(String var
, String path
) {
346 return var
== null ?
null : ("$" + var
+ "$" + (path
== null ?
"" : ("/" + path
)));
349 private String
getUrl(final String path
) {
351 if (path
.startsWith("/")) {
352 final String relativePath
= new File(myRootPath
).getParent() + "/" + path
;
353 final File file
= new File(relativePath
);
355 url
= VfsUtil
.pathToUrl(relativePath
);
356 } else if (new File(path
).exists()) {
357 url
= VfsUtil
.pathToUrl(path
);
360 final String rootPath
= getRootPath(path
);
361 final String relativeToRootPath
= getRelativeToRootPath(path
);
363 final Module otherModule
= ModuleManager
.getInstance(myProject
).findModuleByName(rootPath
);
364 if (otherModule
!= null) {
365 url
= relativeToOtherModule(otherModule
, relativeToRootPath
);
367 else if (myCurrentRoots
!= null) {
368 url
= relativeToContentRoots(myCurrentRoots
, rootPath
, relativeToRootPath
);
373 final String absPath
= myRootPath
+ "/" + path
;
374 if (new File(absPath
).exists()) {
375 url
= VfsUtil
.pathToUrl(absPath
);
378 url
= VfsUtil
.pathToUrl(path
);
381 final VirtualFile localFile
= VirtualFileManager
.getInstance().findFileByUrl(url
);
382 if (localFile
!= null) {
383 final VirtualFile jarFile
= JarFileSystem
.getInstance().getJarRootForLocalFile(localFile
);
384 if (jarFile
!= null) {
385 url
= jarFile
.getUrl();
392 * @param path path in format /module_root/relative_path
393 * @return module_root
396 private static String
getRootPath(String path
) {
397 int secondSlIdx
= path
.indexOf('/', 1);
398 return secondSlIdx
> 1 ? path
.substring(1, secondSlIdx
) : path
.substring(1);
402 * @param path path in format /module_root/relative_path
403 * @return relative_path or null if /module_root
406 private static String
getRelativeToRootPath(String path
) {
407 final int secondSlIdx
= path
.indexOf('/', 1);
408 return secondSlIdx
!= -1 && secondSlIdx
+ 1 < path
.length() ? path
.substring(secondSlIdx
+ 1) : null;
412 private static String
relativeToContentRoots(final @NotNull List
<String
> currentRoots
,
413 final @NotNull String rootPath
,
414 final @Nullable String relativeToRootPath
) {
415 for (String currentRoot
: currentRoots
) {
416 if (currentRoot
.endsWith(rootPath
)) { //rootPath = content_root <=> applicable root: abs_path/content_root
417 if (relativeToRootPath
== null) {
418 return VfsUtil
.pathToUrl(currentRoot
);
420 final File relativeToOtherModuleFile
= new File(currentRoot
, relativeToRootPath
);
421 if (relativeToOtherModuleFile
.exists()) {
422 return VfsUtil
.pathToUrl(relativeToOtherModuleFile
.getPath());
430 private static String
relativeToOtherModule(final @NotNull Module otherModule
, final @Nullable String relativeToOtherModule
) {
431 final VirtualFile
[] contentRoots
= ModuleRootManager
.getInstance(otherModule
).getContentRoots();
432 for (VirtualFile contentRoot
: contentRoots
) {
433 if (relativeToOtherModule
== null) {
434 return contentRoot
.getUrl();
436 final File relativeToOtherModuleFile
= new File(contentRoot
.getPath(), relativeToOtherModule
);
437 if (relativeToOtherModuleFile
.exists()) {
438 return VfsUtil
.pathToUrl(relativeToOtherModuleFile
.getPath());
445 private List
<String
> getJavadocAttribute(Element element
) {
446 Element attributes
= element
.getChild("attributes");
447 if (attributes
== null) {
450 List
<String
> result
= new ArrayList
<String
>();
451 for (Object o
: attributes
.getChildren("attribute")) {
452 if (Comparing
.strEqual(((Element
)o
).getAttributeValue("name"), "javadoc_location")) {
453 Element attribute
= (Element
)o
;
454 String javadocPath
= attribute
.getAttributeValue("value");
455 if (!SystemInfo
.isWindows
) {
456 javadocPath
= javadocPath
.replaceFirst(EclipseXml
.FILE_PROTOCOL
, EclipseXml
.FILE_PROTOCOL
+ "/");
458 if (javadocPath
.startsWith(EclipseXml
.FILE_PROTOCOL
) &&
459 new File(javadocPath
.substring(EclipseXml
.FILE_PROTOCOL
.length())).exists()) {
460 result
.add(VfsUtil
.pathToUrl(javadocPath
.substring(EclipseXml
.FILE_PROTOCOL
.length())));
464 final String protocol
= VirtualFileManager
.extractProtocol(javadocPath
);
465 if (Comparing
.strEqual(protocol
, HttpFileSystem
.getInstance().getProtocol())) {
466 result
.add(javadocPath
);
468 else if (javadocPath
.startsWith(EclipseXml
.JAR_PREFIX
)) {
469 final String jarJavadocPath
= javadocPath
.substring(EclipseXml
.JAR_PREFIX
.length());
470 if (jarJavadocPath
.startsWith(EclipseXml
.PLATFORM_PROTOCOL
)) {
471 String relativeToPlatform
= jarJavadocPath
.substring(EclipseXml
.PLATFORM_PROTOCOL
.length() + "resources".length());
473 .add(VirtualFileManager
.constructUrl(JarFileSystem
.PROTOCOL
, new File(myRootPath
).getParent() + "/" + relativeToPlatform
));
475 else if (jarJavadocPath
.startsWith(EclipseXml
.FILE_PROTOCOL
)) {
477 .add(VirtualFileManager
.constructUrl(JarFileSystem
.PROTOCOL
, jarJavadocPath
.substring(EclipseXml
.FILE_PROTOCOL
.length())));
480 result
.add(javadocPath
);
489 static String
getJunitClsUrl(final boolean version4
) {
490 String url
= version4 ? JavaSdkUtil
.getJunit4JarPath() : JavaSdkUtil
.getJunit3JarPath();
491 final VirtualFile localFile
= VirtualFileManager
.getInstance().findFileByUrl(VfsUtil
.pathToUrl(url
));
492 if (localFile
!= null) {
493 final VirtualFile jarFile
= JarFileSystem
.getInstance().getJarRootForLocalFile(localFile
);
494 url
= jarFile
!= null ? jarFile
.getUrl() : localFile
.getUrl();