e8c892af551a6aa1a8d006e5106f58c46968fc15
[fedora-idea.git] / plugins / eclipse / src / org / jetbrains / idea / eclipse / conversion / EclipseClasspathReader.java
blobe8c892af551a6aa1a8d006e5106f58c46968fc15
1 /*
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.
18 * User: anna
19 * Date: 11-Nov-2008
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;
50 import java.io.File;
51 import java.io.IOException;
52 import java.util.*;
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;
62 myProject = project;
63 myCurrentRoots = currentRoots;
66 public void init(ModifiableRootModel model) {
67 myContentEntry = model.addContentEntry(VfsUtil.pathToUrl(myRootPath));
68 model.inheritSdk();
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("/");
79 if (slash > 0) {
80 usedVariables.add(path.substring(0, slash));
82 else {
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);
91 if (slash2 > 0) {
92 usedVariables.add(srcPath.substring(varStart, slash2));
94 else {
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)) {
114 try {
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);
131 if (kind == null) {
132 throw new ConversionException("Missing classpathentry/@kind");
136 String path = element.getAttributeValue(EclipseXml.PATH_ATTR);
137 if (path == null) {
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);
149 else {
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("/");
183 if (slash == 0) {
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();
192 final String clsVar;
193 final String clsPath;
194 if (slash > 0) {
195 clsVar = path.substring(0, slash);
196 clsPath = path.substring(slash + 1);
198 else {
199 clsVar = path;
200 clsPath = null;
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) {
210 final String srcVar;
211 final String srcPath;
212 final int varStart = srcPathAttr.startsWith("/") ? 1 : 0;
214 int slash2 = srcPathAttr.indexOf("/", varStart);
215 if (slash2 > 0) {
216 srcVar = srcPathAttr.substring(varStart, slash2);
217 srcPath = srcPathAttr.substring(slash2 + 1);
219 else {
220 srcVar = srcPathAttr.substring(varStart);
221 srcPath = null;
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();
250 else {
251 final Sdk moduleJdk = ProjectJdkTable.getInstance().findJdk(jdkName);
252 if (moduleJdk != null) {
253 rootModel.setSdk(moduleJdk);
255 else {
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();
275 else {
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);
292 break;
297 private void addNamedLibrary(final ModifiableRootModel rootModel,
298 final Collection<String> unknownLibraries,
299 final boolean exported,
300 final String name,
301 final String notFoundLibraryLevel) {
302 Library lib = findLibraryByName(myProject, name);
303 if (lib != null) {
304 rootModel.addLibraryEntry(lib).setExported(exported);
306 else {
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);
315 if (lib == null) {
316 lib = tablesRegistrar.getLibraryTable(project).getLibraryByName(name);
318 if (lib == null) {
319 for (LibraryTable table : tablesRegistrar.getCustomLibraryTables()) {
320 lib = table.getLibraryByName(name);
321 if (lib != null) {
322 break;
326 return lib;
329 @NotNull
330 private static String getPresentableName(@NotNull String path) {
331 final String pathComponent = getLastPathComponent(path);
332 return pathComponent != null ? pathComponent : path;
335 @Nullable
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) {
350 String url = null;
351 if (path.startsWith("/")) {
352 final String relativePath = new File(myRootPath).getParent() + "/" + path;
353 final File file = new File(relativePath);
354 if (file.exists()) {
355 url = VfsUtil.pathToUrl(relativePath);
356 } else if (new File(path).exists()) {
357 url = VfsUtil.pathToUrl(path);
359 else {
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);
372 if (url == null) {
373 final String absPath = myRootPath + "/" + path;
374 if (new File(absPath).exists()) {
375 url = VfsUtil.pathToUrl(absPath);
377 else {
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();
388 return url;
392 * @param path path in format /module_root/relative_path
393 * @return module_root
395 @NotNull
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
405 @Nullable
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;
411 @Nullable
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());
426 return null;
429 @Nullable
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());
441 return null;
444 @Nullable
445 private List<String> getJavadocAttribute(Element element) {
446 Element attributes = element.getChild("attributes");
447 if (attributes == null) {
448 return 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())));
462 else {
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());
472 result
473 .add(VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, new File(myRootPath).getParent() + "/" + relativeToPlatform));
475 else if (jarJavadocPath.startsWith(EclipseXml.FILE_PROTOCOL)) {
476 result
477 .add(VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, jarJavadocPath.substring(EclipseXml.FILE_PROTOCOL.length())));
479 else {
480 result.add(javadocPath);
486 return result;
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();
496 return url;