| /* |
| * Copyright 2000-2013 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 com.intellij.openapi.externalSystem.util; |
| |
| import com.intellij.execution.rmi.RemoteUtil; |
| import com.intellij.ide.util.PropertiesComponent; |
| import com.intellij.openapi.application.Application; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.PathManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.externalSystem.ExternalSystemAutoImportAware; |
| import com.intellij.openapi.externalSystem.ExternalSystemManager; |
| import com.intellij.openapi.externalSystem.model.DataNode; |
| import com.intellij.openapi.externalSystem.model.ExternalSystemException; |
| import com.intellij.openapi.externalSystem.model.Key; |
| import com.intellij.openapi.externalSystem.model.ProjectSystemId; |
| import com.intellij.openapi.externalSystem.model.project.LibraryData; |
| import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings; |
| import com.intellij.openapi.externalSystem.service.ParametersEnhancer; |
| import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings; |
| import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings; |
| import com.intellij.openapi.fileTypes.FileTypes; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectManager; |
| import com.intellij.openapi.roots.OrderRootType; |
| import com.intellij.openapi.roots.libraries.Library; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.registry.Registry; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.JarFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.*; |
| import com.intellij.util.containers.ContainerUtilRt; |
| import com.intellij.util.containers.TransferToEDTQueue; |
| import com.intellij.util.lang.UrlClassLoader; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.Contract; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * @author Denis Zhdanov |
| * @since 4/1/13 1:31 PM |
| */ |
| public class ExternalSystemApiUtil { |
| |
| private static final Logger LOG = Logger.getInstance("#" + ExternalSystemApiUtil.class.getName()); |
| private static final String LAST_USED_PROJECT_PATH_PREFIX = "LAST_EXTERNAL_PROJECT_PATH_"; |
| |
| @NotNull public static final String PATH_SEPARATOR = "/"; |
| |
| @NotNull private static final Pattern ARTIFACT_PATTERN = Pattern.compile("(?:.*/)?(.+?)(?:-([\\d+](?:\\.[\\d]+)*))?(?:\\.[^\\.]+?)?"); |
| |
| @NotNull public static final Comparator<Object> ORDER_AWARE_COMPARATOR = new Comparator<Object>() { |
| |
| @Override |
| public int compare(@NotNull Object o1, @NotNull Object o2) { |
| int order1 = getOrder(o1); |
| int order2 = getOrder(o2); |
| return (order1 < order2) ? -1 : ((order1 == order2) ? 0 : 1); |
| } |
| |
| private int getOrder(@NotNull Object o) { |
| Queue<Class<?>> toCheck = new ArrayDeque<Class<?>>(); |
| toCheck.add(o.getClass()); |
| while (!toCheck.isEmpty()) { |
| Class<?> clazz = toCheck.poll(); |
| Order annotation = clazz.getAnnotation(Order.class); |
| if (annotation != null) { |
| return annotation.value(); |
| } |
| Class<?> c = clazz.getSuperclass(); |
| if (c != null) { |
| toCheck.add(c); |
| } |
| Class<?>[] interfaces = clazz.getInterfaces(); |
| Collections.addAll(toCheck, interfaces); |
| } |
| return ExternalSystemConstants.UNORDERED; |
| } |
| }; |
| |
| @NotNull private static final NullableFunction<DataNode<?>, Key<?>> GROUPER = new NullableFunction<DataNode<?>, Key<?>>() { |
| @Override |
| public Key<?> fun(DataNode<?> node) { |
| return node.getKey(); |
| } |
| }; |
| |
| @NotNull private static final Comparator<Object> COMPARABLE_GLUE = new Comparator<Object>() { |
| @SuppressWarnings("unchecked") |
| @Override |
| public int compare(Object o1, Object o2) { |
| return ((Comparable)o1).compareTo(o2); |
| } |
| }; |
| |
| @NotNull private static final TransferToEDTQueue<Runnable> TRANSFER_TO_EDT_QUEUE = |
| new TransferToEDTQueue<Runnable>("External System queue", new Processor<Runnable>() { |
| @Override |
| public boolean process(Runnable runnable) { |
| runnable.run(); |
| return true; |
| } |
| }, Condition.FALSE, 300); |
| |
| private ExternalSystemApiUtil() { |
| } |
| |
| @NotNull |
| public static String extractNameFromPath(@NotNull String path) { |
| String strippedPath = stripPath(path); |
| final int i = strippedPath.lastIndexOf(PATH_SEPARATOR); |
| final String result; |
| if (i < 0 || i >= strippedPath.length() - 1) { |
| result = strippedPath; |
| } |
| else { |
| result = strippedPath.substring(i + 1); |
| } |
| return result; |
| } |
| |
| @NotNull |
| private static String stripPath(@NotNull String path) { |
| String[] endingsToStrip = {"/", "!", ".jar"}; |
| StringBuilder buffer = new StringBuilder(path); |
| for (String ending : endingsToStrip) { |
| if (buffer.lastIndexOf(ending) == buffer.length() - ending.length()) { |
| buffer.setLength(buffer.length() - ending.length()); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| @NotNull |
| public static String getLibraryName(@NotNull Library library) { |
| final String result = library.getName(); |
| if (result != null) { |
| return result; |
| } |
| for (OrderRootType type : OrderRootType.getAllTypes()) { |
| for (String url : library.getUrls(type)) { |
| String candidate = extractNameFromPath(url); |
| if (!StringUtil.isEmpty(candidate)) { |
| return candidate; |
| } |
| } |
| } |
| assert false; |
| return "unknown-lib"; |
| } |
| |
| public static boolean isRelated(@NotNull Library library, @NotNull LibraryData libraryData) { |
| return getLibraryName(library).equals(libraryData.getInternalName()); |
| } |
| |
| public static boolean isExternalSystemLibrary(@NotNull Library library, @NotNull ProjectSystemId externalSystemId) { |
| return library.getName() != null && StringUtil.startsWith(library.getName(), externalSystemId.getReadableName() + ": "); |
| } |
| |
| @Nullable |
| public static ArtifactInfo parseArtifactInfo(@NotNull String fileName) { |
| Matcher matcher = ARTIFACT_PATTERN.matcher(fileName); |
| if (!matcher.matches()) { |
| return null; |
| } |
| return new ArtifactInfo(matcher.group(1), null, matcher.group(2)); |
| } |
| |
| public static void orderAwareSort(@NotNull List<?> data) { |
| Collections.sort(data, ORDER_AWARE_COMPARATOR); |
| } |
| |
| /** |
| * @param path target path |
| * @return absolute path that points to the same location as the given one and that uses only slashes |
| */ |
| @NotNull |
| public static String toCanonicalPath(@NotNull String path) { |
| String p = normalizePath(new File(path).getAbsolutePath()); |
| assert p != null; |
| return PathUtil.getCanonicalPath(p); |
| } |
| |
| @NotNull |
| public static String getLocalFileSystemPath(@NotNull VirtualFile file) { |
| if (file.getFileType() == FileTypes.ARCHIVE) { |
| final VirtualFile jar = JarFileSystem.getInstance().getVirtualFileForJar(file); |
| if (jar != null) { |
| return jar.getPath(); |
| } |
| } |
| return toCanonicalPath(file.getPath()); |
| } |
| |
| @Nullable |
| public static ExternalSystemManager<?, ?, ?, ?, ?> getManager(@NotNull ProjectSystemId externalSystemId) { |
| for (ExternalSystemManager manager : ExternalSystemManager.EP_NAME.getExtensions()) { |
| if (externalSystemId.equals(manager.getSystemId())) { |
| return manager; |
| } |
| } |
| return null; |
| } |
| |
| @SuppressWarnings("ManualArrayToCollectionCopy") |
| @NotNull |
| public static Collection<ExternalSystemManager<?, ?, ?, ?, ?>> getAllManagers() { |
| List<ExternalSystemManager<?, ?, ?, ?, ?>> result = ContainerUtilRt.newArrayList(); |
| for (ExternalSystemManager manager : ExternalSystemManager.EP_NAME.getExtensions()) { |
| result.add(manager); |
| } |
| return result; |
| } |
| |
| @NotNull |
| public static Map<Key<?>, List<DataNode<?>>> group(@NotNull Collection<DataNode<?>> nodes) { |
| return groupBy(nodes, GROUPER); |
| } |
| |
| @NotNull |
| public static <K, V> Map<DataNode<K>, List<DataNode<V>>> groupBy(@NotNull Collection<DataNode<V>> nodes, @NotNull final Key<K> key) { |
| return groupBy(nodes, new NullableFunction<DataNode<V>, DataNode<K>>() { |
| @Nullable |
| @Override |
| public DataNode<K> fun(DataNode<V> node) { |
| return node.getDataNode(key); |
| } |
| }); |
| } |
| |
| @NotNull |
| public static <K, V> Map<K, List<V>> groupBy(@NotNull Collection<V> nodes, @NotNull NullableFunction<V, K> grouper) { |
| Map<K, List<V>> result = ContainerUtilRt.newHashMap(); |
| for (V data : nodes) { |
| K key = grouper.fun(data); |
| if (key == null) { |
| LOG.warn(String.format( |
| "Skipping entry '%s' during grouping. Reason: it's not possible to build a grouping key with grouping strategy '%s'. " |
| + "Given entries: %s", |
| data, |
| grouper.getClass(), |
| nodes)); |
| continue; |
| } |
| List<V> grouped = result.get(key); |
| if (grouped == null) { |
| result.put(key, grouped = ContainerUtilRt.newArrayList()); |
| } |
| grouped.add(data); |
| } |
| |
| if (!result.isEmpty() && result.keySet().iterator().next() instanceof Comparable) { |
| List<K> ordered = ContainerUtilRt.newArrayList(result.keySet()); |
| Collections.sort(ordered, COMPARABLE_GLUE); |
| Map<K, List<V>> orderedResult = ContainerUtilRt.newLinkedHashMap(); |
| for (K k : ordered) { |
| orderedResult.put(k, result.get(k)); |
| } |
| return orderedResult; |
| } |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @NotNull |
| public static <T> Collection<DataNode<T>> getChildren(@NotNull DataNode<?> node, @NotNull Key<T> key) { |
| Collection<DataNode<T>> result = null; |
| for (DataNode<?> child : node.getChildren()) { |
| if (!key.equals(child.getKey())) { |
| continue; |
| } |
| if (result == null) { |
| result = ContainerUtilRt.newArrayList(); |
| } |
| result.add((DataNode<T>)child); |
| } |
| return result == null ? Collections.<DataNode<T>>emptyList() : result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Nullable |
| public static <T> DataNode<T> find(@NotNull DataNode<?> node, @NotNull Key<T> key) { |
| for (DataNode<?> child : node.getChildren()) { |
| if (key.equals(child.getKey())) { |
| return (DataNode<T>)child; |
| } |
| } |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Nullable |
| public static <T> DataNode<T> find(@NotNull DataNode<?> node, @NotNull Key<T> key, BooleanFunction<DataNode<T>> predicate) { |
| for (DataNode<?> child : node.getChildren()) { |
| if (key.equals(child.getKey()) && predicate.fun((DataNode<T>)child)) { |
| return (DataNode<T>)child; |
| } |
| } |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Nullable |
| public static <T> DataNode<T> findParent(@NotNull DataNode<?> node, @NotNull Key<T> key) { |
| return findParent(node, key, null); |
| } |
| |
| |
| @SuppressWarnings("unchecked") |
| @Nullable |
| public static <T> DataNode<T> findParent(@NotNull DataNode<?> node, |
| @NotNull Key<T> key, |
| @Nullable BooleanFunction<DataNode<T>> predicate) { |
| DataNode<?> parent = node.getParent(); |
| if (parent == null) return null; |
| return key.equals(parent.getKey()) && (predicate == null || predicate.fun((DataNode<T>)parent)) |
| ? (DataNode<T>)parent : findParent(parent, key, predicate); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @NotNull |
| public static <T> Collection<DataNode<T>> findAll(@NotNull DataNode<?> parent, @NotNull Key<T> key) { |
| Collection<DataNode<T>> result = null; |
| for (DataNode<?> child : parent.getChildren()) { |
| if (!key.equals(child.getKey())) { |
| continue; |
| } |
| if (result == null) { |
| result = ContainerUtilRt.newArrayList(); |
| } |
| result.add((DataNode<T>)child); |
| } |
| return result == null ? Collections.<DataNode<T>>emptyList() : result; |
| } |
| |
| public static void executeProjectChangeAction(@NotNull final DisposeAwareProjectChange task) { |
| executeProjectChangeAction(false, task); |
| } |
| |
| public static void executeProjectChangeAction(boolean synchronous, @NotNull final DisposeAwareProjectChange task) { |
| executeOnEdt(synchronous, new Runnable() { |
| public void run() { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| task.run(); |
| } |
| }); |
| } |
| }); |
| } |
| |
| public static void executeOnEdt(boolean synchronous, @NotNull Runnable task) { |
| if (synchronous) { |
| if (ApplicationManager.getApplication().isDispatchThread()) { |
| task.run(); |
| } |
| else { |
| UIUtil.invokeAndWaitIfNeeded(task); |
| } |
| } |
| else { |
| UIUtil.invokeLaterIfNeeded(task); |
| } |
| } |
| |
| /** |
| * Adds runnable to Event Dispatch Queue |
| * if we aren't in UnitTest of Headless environment mode |
| * |
| * @param runnable Runnable |
| */ |
| public static void addToInvokeLater(final Runnable runnable) { |
| final Application application = ApplicationManager.getApplication(); |
| final boolean unitTestMode = application.isUnitTestMode(); |
| if (unitTestMode) { |
| UIUtil.invokeLaterIfNeeded(runnable); |
| } |
| else if (application.isHeadlessEnvironment() || application.isDispatchThread()) { |
| runnable.run(); |
| } |
| else { |
| TRANSFER_TO_EDT_QUEUE.offer(runnable); |
| } |
| } |
| |
| /** |
| * Configures given classpath to reference target i18n bundle file(s). |
| * |
| * @param classPath process classpath |
| * @param bundlePath path to the target bundle file |
| * @param contextClass class from the same content root as the target bundle file |
| */ |
| public static void addBundle(@NotNull PathsList classPath, @NotNull String bundlePath, @NotNull Class<?> contextClass) { |
| String pathToUse = bundlePath.replace('.', '/'); |
| if (!pathToUse.endsWith(".properties")) { |
| pathToUse += ".properties"; |
| } |
| if (!pathToUse.startsWith("/")) { |
| pathToUse = '/' + pathToUse; |
| } |
| String root = PathManager.getResourceRoot(contextClass, pathToUse); |
| if (root != null) { |
| classPath.add(root); |
| } |
| } |
| |
| @SuppressWarnings("ConstantConditions") |
| @Nullable |
| public static String normalizePath(@Nullable String s) { |
| return StringUtil.isEmpty(s) ? null : s.replace('\\', ExternalSystemConstants.PATH_SEPARATOR); |
| } |
| |
| /** |
| * We can divide all 'import from external system' use-cases into at least as below: |
| * <pre> |
| * <ul> |
| * <li>this is a new project being created (import project from external model);</li> |
| * <li>a new module is being imported from an external project into an existing ide project;</li> |
| * </ul> |
| * </pre> |
| * This method allows to differentiate between them (e.g. we don't want to change language level when new module is imported to |
| * an existing project). |
| * |
| * @return <code>true</code> if new project is being imported; <code>false</code> if new module is being imported |
| */ |
| public static boolean isNewProjectConstruction() { |
| return ProjectManager.getInstance().getOpenProjects().length == 0; |
| } |
| |
| // @NotNull |
| // public static String getLastUsedExternalProjectPath(@NotNull ProjectSystemId externalSystemId) { |
| // return PropertiesComponent.getInstance().getValue(LAST_USED_PROJECT_PATH_PREFIX + externalSystemId.getReadableName(), ""); |
| // } |
| |
| public static void storeLastUsedExternalProjectPath(@Nullable String path, @NotNull ProjectSystemId externalSystemId) { |
| if (path != null) { |
| PropertiesComponent.getInstance().setValue(LAST_USED_PROJECT_PATH_PREFIX + externalSystemId.getReadableName(), path); |
| } |
| } |
| |
| @NotNull |
| public static String getProjectRepresentationName(@NotNull String targetProjectPath, @Nullable String rootProjectPath) { |
| if (rootProjectPath == null) { |
| File rootProjectDir = new File(targetProjectPath); |
| if (rootProjectDir.isFile()) { |
| rootProjectDir = rootProjectDir.getParentFile(); |
| } |
| return rootProjectDir.getName(); |
| } |
| File rootProjectDir = new File(rootProjectPath); |
| if (rootProjectDir.isFile()) { |
| rootProjectDir = rootProjectDir.getParentFile(); |
| } |
| File targetProjectDir = new File(targetProjectPath); |
| if (targetProjectDir.isFile()) { |
| targetProjectDir = targetProjectDir.getParentFile(); |
| } |
| StringBuilder buffer = new StringBuilder(); |
| for (File f = targetProjectDir; f != null && !FileUtil.filesEqual(f, rootProjectDir); f = f.getParentFile()) { |
| buffer.insert(0, f.getName()).insert(0, ":"); |
| } |
| buffer.insert(0, rootProjectDir.getName()); |
| return buffer.toString(); |
| } |
| |
| /** |
| * There is a possible case that external project linked to an ide project is a multi-project, i.e. contains more than one |
| * module. |
| * <p/> |
| * This method tries to find root project's config path assuming that given path points to a sub-project's config path. |
| * |
| * @param externalProjectPath external sub-project's config path |
| * @param externalSystemId target external system |
| * @param project target ide project |
| * @return root external project's path if given path is considered to point to a known sub-project's config; |
| * <code>null</code> if it's not possible to find a root project's config path on the basis of the |
| * given path |
| */ |
| @Nullable |
| public static String getRootProjectPath(@NotNull String externalProjectPath, |
| @NotNull ProjectSystemId externalSystemId, |
| @NotNull Project project) |
| { |
| ExternalSystemManager<?, ?, ?, ?, ?> manager = getManager(externalSystemId); |
| if (manager == null) { |
| return null; |
| } |
| if (manager instanceof ExternalSystemAutoImportAware) { |
| return ((ExternalSystemAutoImportAware)manager).getAffectedExternalProjectPath(externalProjectPath, project); |
| } |
| return null; |
| } |
| |
| /** |
| * {@link RemoteUtil#unwrap(Throwable) unwraps} given exception if possible and builds error message for it. |
| * |
| * @param e exception to process |
| * @return error message for the given exception |
| */ |
| @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "IOResourceOpenedButNotSafelyClosed"}) |
| @NotNull |
| public static String buildErrorMessage(@NotNull Throwable e) { |
| Throwable unwrapped = RemoteUtil.unwrap(e); |
| String reason = unwrapped.getLocalizedMessage(); |
| if (!StringUtil.isEmpty(reason)) { |
| return reason; |
| } |
| else if (unwrapped.getClass() == ExternalSystemException.class) { |
| return String.format("exception during working with external system: %s", ((ExternalSystemException)unwrapped).getOriginalReason()); |
| } |
| else { |
| StringWriter writer = new StringWriter(); |
| unwrapped.printStackTrace(new PrintWriter(writer)); |
| return writer.toString(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| @NotNull |
| public static AbstractExternalSystemSettings getSettings(@NotNull Project project, @NotNull ProjectSystemId externalSystemId) |
| throws IllegalArgumentException |
| { |
| ExternalSystemManager<?, ?, ?, ?, ?> manager = getManager(externalSystemId); |
| if (manager == null) { |
| throw new IllegalArgumentException(String.format( |
| "Can't retrieve external system settings for id '%s'. Reason: no such external system is registered", |
| externalSystemId.getReadableName() |
| )); |
| } |
| return manager.getSettingsProvider().fun(project); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static <S extends AbstractExternalSystemLocalSettings> S getLocalSettings(@NotNull Project project, |
| @NotNull ProjectSystemId externalSystemId) |
| throws IllegalArgumentException |
| { |
| ExternalSystemManager<?, ?, ?, ?, ?> manager = getManager(externalSystemId); |
| if (manager == null) { |
| throw new IllegalArgumentException(String.format( |
| "Can't retrieve local external system settings for id '%s'. Reason: no such external system is registered", |
| externalSystemId.getReadableName() |
| )); |
| } |
| return (S)manager.getLocalSettingsProvider().fun(project); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static <S extends ExternalSystemExecutionSettings> S getExecutionSettings(@NotNull Project project, |
| @NotNull String linkedProjectPath, |
| @NotNull ProjectSystemId externalSystemId) |
| throws IllegalArgumentException |
| { |
| ExternalSystemManager<?, ?, ?, ?, ?> manager = getManager(externalSystemId); |
| if (manager == null) { |
| throw new IllegalArgumentException(String.format( |
| "Can't retrieve external system execution settings for id '%s'. Reason: no such external system is registered", |
| externalSystemId.getReadableName() |
| )); |
| } |
| return (S)manager.getExecutionSettingsProvider().fun(Pair.create(project, linkedProjectPath)); |
| } |
| |
| /** |
| * Historically we prefer to work with third-party api not from ide process but from dedicated slave process (there is a risk |
| * that third-party api has bugs which might make the whole ide process corrupted, e.g. a memory leak at the api might crash |
| * the whole ide process). |
| * <p/> |
| * However, we do allow to explicitly configure the ide to work with third-party external system api from the ide process. |
| * <p/> |
| * This method allows to check whether the ide is configured to use 'out of process' or 'in process' mode for the system. |
| * |
| * @param externalSystemId target external system |
| * |
| * @return <code>true</code> if the ide is configured to work with external system api from the ide process; |
| * <code>false</code> otherwise |
| */ |
| public static boolean isInProcessMode(ProjectSystemId externalSystemId) { |
| return Registry.is(externalSystemId.getId() + ExternalSystemConstants.USE_IN_PROCESS_COMMUNICATION_REGISTRY_KEY_SUFFIX, false); |
| } |
| |
| /** |
| * There is a possible case that methods of particular object should be executed with classpath different from the one implied |
| * by the current class' class loader. External system offers {@link ParametersEnhancer#enhanceLocalProcessing(List)} method |
| * for defining that custom classpath. |
| * <p/> |
| * It's also possible that particular implementation of {@link ParametersEnhancer} is compiled using dependency to classes |
| * which are provided by the {@link ParametersEnhancer#enhanceLocalProcessing(List) expanded classpath}. E.g. a class |
| * <code>'A'</code> might use method of class <code>'B'</code> and 'A' is located at the current (system/plugin) classpath but |
| * <code>'B'</code> is not. We need to reload <code>'A'</code> using its expanded classpath then, i.e. create new class loaded |
| * with that expanded classpath and load <code>'A'</code> by it. |
| * <p/> |
| * This method allows to do that. |
| * |
| * @param clazz custom classpath-aware class which instance should be created (is assumed to have a no-args constructor) |
| * @param <T> target type |
| * @return newly created instance of the given class loaded by custom classpath-aware loader |
| * @throws IllegalAccessException as defined by reflection processing |
| * @throws InstantiationException as defined by reflection processing |
| * @throws NoSuchMethodException as defined by reflection processing |
| * @throws InvocationTargetException as defined by reflection processing |
| * @throws ClassNotFoundException as defined by reflection processing |
| */ |
| @NotNull |
| public static <T extends ParametersEnhancer> T reloadIfNecessary(@NotNull final Class<T> clazz) |
| throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException |
| { |
| T instance = clazz.newInstance(); |
| List<URL> urls = ContainerUtilRt.newArrayList(); |
| instance.enhanceLocalProcessing(urls); |
| if (urls.isEmpty()) { |
| return instance; |
| } |
| |
| final ClassLoader baseLoader = clazz.getClassLoader(); |
| Method method = baseLoader.getClass().getMethod("getUrls"); |
| if (method != null) { |
| //noinspection unchecked |
| urls.addAll((Collection<? extends URL>)method.invoke(baseLoader)); |
| } |
| UrlClassLoader loader = new UrlClassLoader(UrlClassLoader.build().urls(urls).parent(baseLoader.getParent())) { |
| @Override |
| protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { |
| if (name.equals(clazz.getName())) { |
| return super.loadClass(name, resolve); |
| } |
| else { |
| try { |
| return baseLoader.loadClass(name); |
| } |
| catch (ClassNotFoundException e) { |
| return super.loadClass(name, resolve); |
| } |
| } |
| } |
| }; |
| //noinspection unchecked |
| return (T)loader.loadClass(clazz.getName()).newInstance(); |
| } |
| |
| @Contract("_, null -> false") |
| public static boolean isExternalSystemAwareModule(@NotNull ProjectSystemId systemId, @Nullable Module module) { |
| return module != null && systemId.getId().equals(module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY)); |
| } |
| |
| @Contract("_, null -> false") |
| public static boolean isExternalSystemAwareModule(@NotNull String systemId, @Nullable Module module) { |
| return module != null && systemId.equals(module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY)); |
| } |
| |
| @Nullable |
| public static String getExternalProjectPath(@Nullable Module module) { |
| return module != null ? module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY) : null; |
| } |
| |
| @Nullable |
| public static String getExternalProjectId(@Nullable Module module) { |
| return module != null ? module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_ID_KEY) : null; |
| } |
| } |