blob: 399a03c8e0ab2db84ca9639434554b40b5a7da4f [file]
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.eclipse;
import com.intellij.openapi.components.ExpandMacroToPathMap;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.*;
import java.util.jar.Manifest;
import java.util.regex.PatternSyntaxException;
public abstract class AbstractEclipseClasspathReader<T> {
protected final String myRootPath;
@Nullable protected final List<String> myCurrentRoots;
@Nullable protected final Set<String> myModuleNames;
public AbstractEclipseClasspathReader(@NotNull String rootPath, @Nullable List<String> currentRoots, @Nullable Set<String> moduleNames) {
myRootPath = FileUtil.toSystemIndependentName(rootPath);
myCurrentRoots = currentRoots;
myModuleNames = moduleNames;
}
protected abstract String prepareValidUrlInsideJar(String url);
protected abstract void addNamedLibrary(T rootModel,
Collection<? super String> unknownLibraries,
boolean exported,
String name,
boolean applicationLevel);
protected abstract void addInvalidModuleEntry(T rootModel, boolean exported, String moduleName);
protected abstract void setUpModuleJdk(T rootModel,
Collection<? super String> unknownJdks,
EclipseModuleManager eclipseModuleManager,
String jdkName);
public abstract void setupOutput(T rootModel, String path);
protected abstract void addSourceFolder(T rootModel, String srcUrl, boolean testFolder);
protected abstract void addSourceFolderToCurrentContentRoot(T rootModel, String srcUrl, boolean testFolder);
protected abstract void addJUnitDefaultLib(T rootModel, String junitName, ExpandMacroToPathMap macroMap);
protected abstract void addModuleLibrary(T rootModel,
Element element,
boolean exported,
String libName,
String url,
String srcUrl, String nativeRoot, ExpandMacroToPathMap macroMap);
protected abstract String expandEclipsePath2Url(T rootModel, String path);
protected abstract Set<String> getDefinedCons();
protected abstract int rearrange(T rootModel);
protected void readClasspathEntry(T rootModel,
final Collection<? super String> unknownLibraries,
Collection<? super String> unknownJdks,
Set<? super String> refsToModules,
final String testPattern,
Element element, int index,
@Nullable EclipseModuleManager eclipseModuleManager,
final ExpandMacroToPathMap macroMap,
final Set<? super String> libs) throws ConversionException {
String kind = element.getAttributeValue(EclipseXml.KIND_ATTR);
if (kind == null) {
throw new ConversionException("Missing classpathentry/@kind");
}
String path = element.getAttributeValue(EclipseXml.PATH_ATTR);
if (path == null) {
throw new ConversionException("Missing classpathentry/@path");
}
final boolean exported = EclipseXml.TRUE_VALUE.equals(element.getAttributeValue(EclipseXml.EXPORTED_ATTR));
if (kind.equals(EclipseXml.SRC_KIND)) {
if (path.startsWith("/")) {
final String moduleName = path.substring(1);
refsToModules.add(moduleName);
addInvalidModuleEntry(rootModel, exported, moduleName);
}
else {
String srcUrl = pathToUrl(getPathRelativeToRoot(path));
boolean isTestFolder;
try {
isTestFolder = !StringUtil.isEmpty(testPattern) && path.matches(testPattern);
}
catch (PatternSyntaxException e) {
isTestFolder = false;
}
String linked = expandLinkedResourcesPath(macroMap, path);
if (linked == null) {
addSourceFolderToCurrentContentRoot(rootModel, srcUrl, isTestFolder);
}
else {
srcUrl = prepareValidUrlInsideJar(pathToUrl(linked));
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseLinkedSrcVarPath(srcUrl, path);
}
addSourceFolder(rootModel, srcUrl, isTestFolder);
}
if (eclipseModuleManager != null) {
eclipseModuleManager.setExpectedModuleSourcePlace(rearrange(rootModel));
eclipseModuleManager.registerSrcPlace(srcUrl, index);
}
}
}
else if (kind.equals(EclipseXml.OUTPUT_KIND)) {
String output = getPathRelativeToRoot(path);
String linked = expandLinkedResourcesPath(macroMap, path);
if (linked != null) {
output = linked;
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseLinkedVarPath(pathToUrl(output), path);
}
}
setupOutput(rootModel, output);
}
else if (kind.equals(EclipseXml.LIB_KIND)) {
String linked = expandLinkedResourcesPath(macroMap, path);
String url;
if (linked != null) {
url = prepareValidUrlInsideJar(pathToUrl(linked));
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseLinkedVarPath(url, path);
}
}
else {
url = expandEclipsePath2Url(rootModel, path);
}
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseLibUrl(url);
}
final String sourcePath = element.getAttributeValue(EclipseXml.SOURCEPATH_ATTR);
String srcUrl = null;
if (sourcePath != null) {
final String linkedSrc = expandLinkedResourcesPath(macroMap, sourcePath);
if (linkedSrc != null) {
srcUrl = prepareValidUrlInsideJar(pathToUrl(linkedSrc));
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseLinkedSrcVarPath(srcUrl, sourcePath);
}
}
else {
srcUrl = expandEclipsePath2Url(rootModel, sourcePath);
}
}
String nativeRoot = getNativeLibraryRoot(element);
if (nativeRoot != null) {
nativeRoot = expandEclipsePath2Url(rootModel, nativeRoot);
}
addModuleLibrary(rootModel, element, exported, getPresentableName(path, libs), url, srcUrl, nativeRoot, macroMap);
}
else if (kind.equals(EclipseXml.VAR_KIND)) {
int slash = path.indexOf("/");
if (slash == 0) {
throw new ConversionException("Incorrect 'classpathentry/var@path' format");
}
final String libName = getPresentableName(path, libs);
String url = eclipseVariabledPath2Url(macroMap, path, 0);
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseVariablePath(url, path);
}
final String srcPathAttr = element.getAttributeValue(EclipseXml.SOURCEPATH_ATTR);
String srcUrl = null;
if (srcPathAttr != null) {
srcUrl = eclipseVariabledPath2Url(macroMap, srcPathAttr, srcVarStart(srcPathAttr));
if (eclipseModuleManager != null) {
eclipseModuleManager.registerEclipseSrcVariablePath(srcUrl, srcPathAttr);
}
}
String nativeRoot = getNativeLibraryRoot(element);
if (nativeRoot != null) {
nativeRoot = expandEclipsePath2Url(rootModel, nativeRoot);
}
addModuleLibrary(rootModel, element, exported, libName, url, srcUrl, nativeRoot, macroMap);
}
else if (kind.equals(EclipseXml.CON_KIND)) {
if (path.equals(EclipseXml.ECLIPSE_PLATFORM)) {
readRequiredBundles(rootModel, refsToModules);
addNamedLibrary(rootModel, unknownLibraries, exported, IdeaXml.ECLIPSE_LIBRARY, true);
}
else if (path.startsWith(EclipseXml.JRE_CONTAINER)) {
final String jdkName = getLastPathComponent(path);
setUpModuleJdk(rootModel, unknownJdks, eclipseModuleManager, jdkName);
}
else if (path.startsWith(EclipseXml.USER_LIBRARY)) {
addNamedLibrary(rootModel, unknownLibraries, exported, getPresentableName(path), false);
}
else if (path.startsWith(EclipseXml.JUNIT_CONTAINER)) {
final String junitName = IdeaXml.JUNIT + getPresentableName(path);
addJUnitDefaultLib(rootModel, junitName, macroMap);
}
else {
Set<String> registeredCons = getDefinedCons();
if (registeredCons.contains(path)) {
if (eclipseModuleManager != null) {
eclipseModuleManager.registerCon(path);
eclipseModuleManager.registerSrcPlace(path, index);
}
}
else {
if (eclipseModuleManager != null) {
eclipseModuleManager.registerUnknownCons(path);
}
addNamedLibrary(rootModel, new ArrayList<>(), exported, path, true);
}
}
}
else {
throw new ConversionException("Unknown classpathentry/@kind: " + kind);
}
}
private String getPathRelativeToRoot(String path) {
return path.isEmpty() ? myRootPath : (myRootPath + "/" + path);
}
private static String getNativeLibraryRoot(Element element) {
final Element attributes = element.getChild(EclipseXml.ATTRIBUTES_TAG);
if (attributes != null) {
for (Element attributeElement : attributes.getChildren(EclipseXml.ATTRIBUTE_TAG)) {
if (EclipseXml.DLL_LINK.equals(attributeElement.getAttributeValue(EclipseXml.NAME_ATTR))) {
return attributeElement.getAttributeValue(EclipseXml.VALUE_ATTR);
}
}
}
return null;
}
protected static int srcVarStart(String srcPath) {
return srcPath.startsWith("/") ? 1 : 0;
}
@NotNull
protected static String getPresentableName(@NotNull String path) {
return getPresentableName(path, null);
}
@NotNull
protected static String getPresentableName(@NotNull String path, Set<? super String> names) {
String pathComponent = getLastPathComponent(path);
if (pathComponent != null && names != null && !names.add(pathComponent)) {
return path;
}
else {
return pathComponent == null ? path : pathComponent;
}
}
@Nullable
public static String getLastPathComponent(final String path) {
final int idx = path.lastIndexOf('/');
return idx < 0 || idx == path.length() - 1 ? null : path.substring(idx + 1);
}
@NotNull
protected static String getVariableRelatedPath(@NotNull String var, String path) {
return "$" + var + "$" + (path == null ? "" : "/" + path);
}
@NotNull
protected static String pathToUrl(@NotNull String path) {
return "file://" + FileUtil.toSystemIndependentName(path);
}
protected static EPathVariable createEPathVariable(String pathAttr, int varStart) {
int slash = pathAttr.indexOf("/", varStart);
if (slash > 0) {
return new EPathVariable(pathAttr.substring(varStart, slash), pathAttr.substring(slash + 1));
}
else {
return new EPathVariable(pathAttr.substring(varStart), null);
}
}
@SuppressWarnings("SpellCheckingInspection")
protected String eclipseVariabledPath2Url(ExpandMacroToPathMap pathMap, String path, int varStart) {
EPathVariable var = createEPathVariable(path, varStart);
return prepareValidUrlInsideJar(pathMap.substitute(pathToUrl(getVariableRelatedPath(var.myVariable, var.myRelatedPath)), SystemInfo.isFileSystemCaseSensitive));
}
@Nullable
protected String expandLinkedResourcesPath(final ExpandMacroToPathMap macroMap,
final String path) {
final EclipseProjectFinder.LinkedResource linkedResource = EclipseProjectFinder.findLinkedResource(myRootPath, path);
if (linkedResource != null) {
if (linkedResource.containsPathVariable()) {
final String toPathVariableFormat =
getVariableRelatedPath(linkedResource.getVariableName(), linkedResource.getRelativeToVariablePath());
return macroMap.substitute(toPathVariableFormat, SystemInfo.isFileSystemCaseSensitive);
}
return linkedResource.getLocation();
}
return null;
}
protected void readRequiredBundles(T rootModel, Set<? super String> refsToModules) throws ConversionException {
if (myModuleNames == null) {
return;
}
final File manifestFile = new File(myRootPath, "META-INF/MANIFEST.MF");
if (!manifestFile.exists()) {
return;
}
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(manifestFile));
final Manifest manifest = new Manifest(in);
final String attributes = manifest.getMainAttributes().getValue("Require-Bundle");
if (!StringUtil.isEmpty(attributes)) {
final StringTokenizer tokenizer = new StringTokenizer(attributes, ",");
while (tokenizer.hasMoreTokens()) {
String bundle = tokenizer.nextToken().trim();
if (!bundle.isEmpty()) {
final int constraintIndex = bundle.indexOf(';');
if (constraintIndex != -1) {
bundle = bundle.substring(0, constraintIndex).trim();
}
if (myModuleNames.contains(bundle)) {
refsToModules.add(bundle);
addInvalidModuleEntry(rootModel, false, bundle);
}
}
}
}
}
catch (IOException e) {
throw new ConversionException(e.getMessage());
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ignored) {
}
}
}
}
protected static class EPathVariable {
@NotNull
private final String myVariable;
private final String myRelatedPath;
protected EPathVariable(@NotNull String variable, final String relatedPath) {
myVariable = variable;
myRelatedPath = relatedPath;
}
}
}