| /* | |
| * 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; | |
| } | |
| } | |
| } |