package com.intellij.lang.javascript.flex.projectStructure;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.FlexProjectConfigurationEditor;
import com.intellij.lang.javascript.psi.impl.CompositeRootCollection;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.projectRoots.*;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.RootProvider;
import com.intellij.openapi.roots.impl.SdkFinder;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class FlexCompositeSdk extends UserDataHolderBase implements Sdk, CompositeRootCollection {
private static final String NAME_DELIM = "\t";
public static class SdkFinderImpl extends SdkFinder {
public Sdk findSdk(final String name, final String sdkType) {
if (TYPE.getName().equals(sdkType)) {
final List<String> sdksNames = StringUtil.split(name, NAME_DELIM);
return new FlexCompositeSdk(ArrayUtil.toStringArray(sdksNames));
}
return null;
}
}
private final String[] myNames;
@Nullable
private volatile Sdk[] mySdks;
public FlexCompositeSdk(String[] names) {
myNames = names;
init();
}
private void init() {
Application application = ApplicationManager.getApplication();
final Disposable d = Disposer.newDisposable();
Disposer.register(application, d);
application.getMessageBus().connect(d).subscribe(ProjectJdkTable.JDK_TABLE_TOPIC, new ProjectJdkTable.Listener() {
@Override
public void jdkAdded(@NotNull final Sdk jdk) {
resetSdks();
}
@Override
public void jdkRemoved(@NotNull final Sdk jdk) {
if (jdk == FlexCompositeSdk.this) {
Disposer.dispose(d);
}
resetSdks();
}
@Override
public void jdkNameChanged(@NotNull final Sdk jdk, @NotNull final String previousName) {
resetSdks();
}
});
}
@NotNull
public SdkType getSdkType() {
return TYPE;
}
@NotNull
public String getName() {
return getCompositeName(myNames);
}
public static String getCompositeName(final String[] names) {
return StringUtil.join(names, NAME_DELIM);
}
public String getVersionString() {
return null;
}
public String getHomePath() {
return null;
}
public VirtualFile getHomeDirectory() {
return null;
}
@NotNull
public RootProvider getRootProvider() {
return new RootProvider() {
@NotNull
public String[] getUrls(@NotNull final OrderRootType rootType) {
final Collection<String> result = new HashSet<>();
forAllSdks(sdk -> {
result.addAll(Arrays.asList(sdk.getRootProvider().getUrls(rootType)));
return true;
});
return ArrayUtil.toStringArray(result);
}
@NotNull
public VirtualFile[] getFiles(@NotNull final OrderRootType rootType) {
final Collection<VirtualFile> result = new HashSet<>();
forAllSdks(sdk -> {
result.addAll(Arrays.asList(sdk.getRootProvider().getFiles(rootType)));
return true;
});
return result.toArray(new VirtualFile[result.size()]);
}
public void addRootSetChangedListener(@NotNull final RootSetChangedListener listener) {
forAllSdks(sdk -> {
final RootProvider rootProvider = sdk.getRootProvider();
rootProvider.removeRootSetChangedListener(listener);
rootProvider.addRootSetChangedListener(listener);
return true;
});
}
public void addRootSetChangedListener(@NotNull final RootSetChangedListener listener, @NotNull final Disposable parentDisposable) {
forAllSdks(sdk -> {
sdk.getRootProvider().addRootSetChangedListener(listener, parentDisposable);
return true;
});
}
public void removeRootSetChangedListener(@NotNull final RootSetChangedListener listener) {
forAllSdks(sdk -> {
sdk.getRootProvider().removeRootSetChangedListener(listener);
return true;
});
}
};
}
private void forAllSdks(Processor<Sdk> processor) {
Sdk[] sdks = getSdks();
for (Sdk sdk : sdks) {
if (!processor.process(sdk)) {
return;
}
}
}
@NotNull
public Sdk[] getSdks() {
if (mySdks != null) {
return mySdks;
}
Sdk[] allSdks;
boolean cache;
final FlexProjectConfigurationEditor currentEditor = FlexBuildConfigurationsExtension.getInstance().getConfigurator().getConfigEditor();
if (currentEditor == null) {
allSdks = ProjectJdkTable.getInstance().getAllJdks();
cache = true;
}
else {
final Collection<Sdk> sdks =
ProjectStructureConfigurable.getInstance(currentEditor.getProject()).getProjectJdksModel().getProjectSdks().values();
allSdks = sdks.toArray(new Sdk[sdks.size()]);
cache = false;
}
List<Sdk> result = ContainerUtil.findAll(allSdks, sdk -> ArrayUtil.contains(sdk.getName(), myNames));
Sdk[] resultArray = result.toArray(new Sdk[result.size()]);
if (cache) {
mySdks = resultArray;
}
return resultArray;
}
private void resetSdks() {
mySdks = null;
}
@NotNull
public SdkModificator getSdkModificator() {
throw new UnsupportedOperationException();
}
public SdkAdditionalData getSdkAdditionalData() {
return null;
}
@NotNull
public Object clone() {
throw new UnsupportedOperationException();
}
private static final OrderRootType[] RELEVANT_ROOT_TYPES = new OrderRootType[]{OrderRootType.CLASSES, OrderRootType.SOURCES};
@Override
public VirtualFile[] getFiles(final OrderRootType rootType, final VirtualFile hint) {
Sdk[] sdks = getSdks();
for (Sdk sdk : sdks) {
for (OrderRootType t : RELEVANT_ROOT_TYPES) {
VirtualFile[] files = sdk.getRootProvider().getFiles(t);
if (isAncestorOf(files, hint)) {
return t == rootType ? files : sdk.getRootProvider().getFiles(rootType);
}
}
}
return VirtualFile.EMPTY_ARRAY;
}
private static boolean isAncestorOf(VirtualFile[] ancestors, VirtualFile file) {
VirtualFile fileInLocalFs = JarFileSystem.getInstance().getVirtualFileForJar(file);
for (VirtualFile ancestor : ancestors) {
if (VfsUtilCore.isAncestor(ancestor, file, false)) return true;
if (fileInLocalFs != null && VfsUtilCore.isAncestor(ancestor, fileInLocalFs, false)) return true;
}
return false;
}
public String getName(final VirtualFile hint) {
Sdk[] sdks = getSdks();
if (sdks.length >= 2) {
for (Sdk sdk : sdks) {
for (OrderRootType t : RELEVANT_ROOT_TYPES) {
if (isAncestorOf(sdk.getRootProvider().getFiles(t), hint)) {
return sdk.getName();
}
}
}
}
return getName();
}
public static final String TYPE_ID = "__CompositeFlexSdk__";
private static final SdkType TYPE = new SdkType(TYPE_ID) {
public String suggestHomePath() {
return null;
}
public boolean isValidSdkHome(final String path) {
return false;
}
public String suggestSdkName(final String currentSdkName, final String sdkHome) {
return currentSdkName;
}
@Nullable
public AdditionalDataConfigurable createAdditionalDataConfigurable(@NotNull final SdkModel sdkModel, @NotNull final SdkModificator sdkModificator) {
return null;
}
public void saveAdditionalData(@NotNull final SdkAdditionalData additionalData, @NotNull final Element additional) {
}
public String getPresentableName() {
return getName();
}
};
}