package com.intellij.lang.javascript.flex.projectStructure.ui;
import com.intellij.flex.FlexCommonBundle;
import com.intellij.flex.model.bc.LinkageType;
import com.intellij.lang.javascript.flex.FlexBundle;
import com.intellij.lang.javascript.flex.build.FlashProjectStructureProblem;
import com.intellij.lang.javascript.flex.build.FlexCompilerHandler;
import com.intellij.lang.javascript.flex.build.ValidateFlashConfigurationsPrecompileTask;
import com.intellij.lang.javascript.flex.projectStructure.FlexBCConfigurator;
import com.intellij.lang.javascript.flex.projectStructure.FlexBuildConfigurationsExtension;
import com.intellij.lang.javascript.flex.projectStructure.model.*;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.FlexProjectConfigurationEditor;
import com.intellij.lang.javascript.flex.projectStructure.options.FlexProjectRootsUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.navigation.Place;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class BuildConfigurationProjectStructureElement extends ProjectStructureElement {
private final ModifiableFlexBuildConfiguration myBc;
private final Module myModule;
public BuildConfigurationProjectStructureElement(final ModifiableFlexBuildConfiguration bc,
final Module module,
final @NotNull StructureConfigurableContext context) {
super(context);
myBc = bc;
myModule = module;
}
@Override
public String getPresentableName() {
return myBc.getName();
}
@Override
public String getPresentableText() {
return FlexCommonBundle.message("bc.0.module.1", myBc.getName(), myModule.getName());
}
@Override
public String getTypeName() {
return FlexBundle.message("bc.structure.element.type.name");
}
@Override
public String getId() {
return "flex_bc:" + myBc.getName() + "\t" + myModule.getName();
}
@Override
public void check(final ProjectStructureProblemsHolder problemsHolder) {
final FlexBCConfigurator configurator = FlexBuildConfigurationsExtension.getInstance().getConfigurator();
final FlexProjectConfigurationEditor editor = configurator.getConfigEditor();
assert editor != null;
/*
final SdkEntry sdkEntry = myBc.getDependencies().getSdkEntry();
if (sdkEntry == null) {
Pair<String, Object> location =
Pair.<String, Object>create(FlexBCConfigurable.LOCATION_ON_TAB, DependenciesConfigurable.Location.SDK);
PlaceInProjectStructure place = new PlaceInBuildConfiguration(this, DependenciesConfigurable.TAB_NAME, location);
problemsHolder.registerProblem(FlexBundle.message("bc.problem.no.sdk"), null, ProjectStructureProblemType.error("sdk"),
place, null);
}
else {
if (FlexSdkUtils.findFlexOrFlexmojosSdk(sdkEntry.getName()) == null) {
Pair<String, Object> location =
Pair.<String, Object>create(FlexBCConfigurable.LOCATION_ON_TAB, DependenciesConfigurable.Location.SDK);
PlaceInProjectStructure place = new PlaceInBuildConfiguration(this, DependenciesConfigurable.TAB_NAME, location);
problemsHolder.registerProblem(FlexBundle.message("bc.problem.sdk.not.found", sdkEntry.getName()), null,
ProjectStructureProblemType.error("flex-bc-sdk"), place, null);
}
}
*/
checkDependencies(problemsHolder, editor);
checkSameOutputPaths(problemsHolder, configurator, editor);
checkIfBCOutputUsedAs3rdPartyLib(problemsHolder, configurator, editor);
ValidateFlashConfigurationsPrecompileTask.checkConfiguration(myModule, myBc, true, problem -> {
// actually this if-condition is always true because real model doesn't report FlexUnitOutputFolderProblem
if (!(problem instanceof FlashProjectStructureProblem.FlexUnitOutputFolderProblem)) {
PlaceInProjectStructure place =
new PlaceInBuildConfiguration(this, problem.tabName, problem.locationOnTab);
final ProjectStructureProblemType problemType = problem.severity == ProjectStructureProblemType.Severity.ERROR
? ProjectStructureProblemType.error(problem.errorId)
: ProjectStructureProblemType.warning(problem.errorId);
problemsHolder.registerProblem(problem.errorMessage, null, problemType, place, null);
}
});
}
private void checkDependencies(final ProjectStructureProblemsHolder problemsHolder, final FlexProjectConfigurationEditor editor) {
final ModulesConfigurator modulesConfigurator = myContext.getModulesConfigurator();
for (DependencyEntry entry : myBc.getDependencies().getEntries()) {
if (entry instanceof BuildConfigurationEntry) {
final String moduleName = ((BuildConfigurationEntry)entry).getModuleName();
final String bcName = ((BuildConfigurationEntry)entry).getBcName();
final Module module = modulesConfigurator.getModule(moduleName);
String errorMessage = null;
if (module == null) {
errorMessage = FlexBundle.message("bc.problem.dependency.module.not.found", moduleName);
}
else if (ContainerUtil.find(editor.getConfigurations(module), configuration -> bcName.equals(configuration.getName())) == null) {
errorMessage = FlexBundle.message("bc.problem.dependency.bc.not.found", bcName, moduleName);
}
if (errorMessage != null) {
Object location = DependenciesConfigurable.Location.TableEntry.forBc(moduleName, bcName);
PlaceInProjectStructure place = new PlaceInBuildConfiguration(this, DependenciesConfigurable.TAB_NAME, location);
problemsHolder.registerProblem(errorMessage, null, ProjectStructureProblemType.error("flex-bc-dependency-bc"), place, null);
}
}
}
}
private void checkSameOutputPaths(final ProjectStructureProblemsHolder problemsHolder,
final FlexBCConfigurator configurator,
final FlexProjectConfigurationEditor editor) {
final String outputPath = myBc.getActualOutputFilePath();
final List<ModifiableFlexBuildConfiguration> bcs = configurator.getBCsByOutputPath(outputPath);
if (bcs != null && bcs.size() > 1) {
final StringBuilder buf = new StringBuilder();
for (ModifiableFlexBuildConfiguration bc : bcs) {
if (bc != myBc) {
if (buf.length() > 0) buf.append(", ");
buf.append(FlexBundle.message("0.module.1", bc.getName(), editor.getModule(bc).getName()));
}
}
final String message =
FlexBundle.message("same.output.files.as.in.bcs", bcs.size() - 1, buf.toString(), FileUtil.toSystemDependentName(outputPath));
final FlexBCConfigurable.Location location = FlexBCConfigurable.Location.OutputFileName;
final PlaceInProjectStructure placeInPS = new PlaceInBuildConfiguration(this, myBc.getName(), location);
problemsHolder.registerProblem(message, null, ProjectStructureProblemType.warning(location.errorId), placeInPS, null);
}
}
private void checkIfBCOutputUsedAs3rdPartyLib(final ProjectStructureProblemsHolder problemsHolder,
final FlexBCConfigurator configurator,
final FlexProjectConfigurationEditor editor) {
for (final DependencyEntry entry : myBc.getDependencies().getEntries()) {
if (entry instanceof ModuleLibraryEntry) {
final LibraryOrderEntry orderEntry =
FlexProjectRootsUtil.findOrderEntry((ModuleLibraryEntry)entry, editor.getModifiableRootModel(myModule));
if (orderEntry != null) {
checkIfBCOutputUsedAs3rdPartyLib(problemsHolder, configurator, entry, orderEntry.getRootFiles(OrderRootType.CLASSES));
}
}
else if (entry instanceof SharedLibraryEntry) {
final Library library = FlexProjectRootsUtil.findOrderEntry(myModule.getProject(), (SharedLibraryEntry)entry);
if (library != null) {
checkIfBCOutputUsedAs3rdPartyLib(problemsHolder, configurator, entry, library.getFiles((OrderRootType.CLASSES)));
}
}
}
}
private void checkIfBCOutputUsedAs3rdPartyLib(final ProjectStructureProblemsHolder problemsHolder,
final FlexBCConfigurator configurator,
final DependencyEntry entry,
final VirtualFile[] classesRoots) {
for (VirtualFile libFile : classesRoots) {
final VirtualFile realFile = FlexCompilerHandler.getRealFile(libFile);
if (realFile != null && !realFile.isDirectory() && "swc".equalsIgnoreCase(realFile.getExtension())) {
final List<ModifiableFlexBuildConfiguration> bcs = configurator.getBCsByOutputPath(realFile.getPath());
if (bcs != null && !bcs.isEmpty()) {
final ModifiableFlexBuildConfiguration otherLibBC = bcs.get(0);
final FlexProjectConfigurationEditor editor = configurator.getConfigEditor();
assert editor != null;
final Module otherLibModule = editor.getModule(otherLibBC);
final String message =
FlexBundle.message("own.lib.used.as.3rd.party", realFile.getName(), otherLibBC.getName(), otherLibModule.getName());
final Object location =
entry instanceof ModuleLibraryEntry
? DependenciesConfigurable.Location.TableEntry.forModuleLibrary(((ModuleLibraryEntry)entry).getLibraryId())
: DependenciesConfigurable.Location.TableEntry
.forSharedLibrary(((SharedLibraryEntry)entry).getLibraryLevel(), ((SharedLibraryEntry)entry).getLibraryName());
final PlaceInProjectStructure placeInPS = new PlaceInBuildConfiguration(this, DependenciesConfigurable.TAB_NAME, location);
final String quickFixName = FlexBundle.message("instead.setup.dependency.on.bc", otherLibBC.getName(), otherLibModule.getName());
final ConfigurationErrorQuickFix quickFix = new ConfigurationErrorQuickFix(quickFixName) {
public void performFix() {
final FlexBCConfigurable configurable = configurator.getBCConfigurable(myBc);
final DependenciesConfigurable dependenciesConfigurable = configurable.getDependenciesConfigurable();
final FlexBCConfigurable otherLibConfigurable = configurator.getBCConfigurable(otherLibBC);
final LinkageType linkageType = entry.getDependencyType().getLinkageType();
dependenciesConfigurable.addBCDependency(otherLibConfigurable, linkageType);
if (entry instanceof ModuleLibraryEntry) {
dependenciesConfigurable.removeDependency(((ModuleLibraryEntry)entry).getLibraryId());
}
else {
dependenciesConfigurable
.removeDependency(((SharedLibraryEntry)entry).getLibraryLevel(), ((SharedLibraryEntry)entry).getLibraryName());
}
final Place place = configurator.getPlaceFor(myModule, myBc.getName());
place.putPath(CompositeConfigurable.TAB_NAME, DependenciesConfigurable.TAB_NAME);
place.putPath(FlexBCConfigurable.LOCATION_ON_TAB,
DependenciesConfigurable.Location.TableEntry.forBc(otherLibConfigurable));
ProjectStructureConfigurable.getInstance(myModule.getProject()).navigateTo(place, true);
}
};
final String errorId =
entry instanceof ModuleLibraryEntry ? ((ModuleLibraryEntry)entry).getLibraryId() : ((SharedLibraryEntry)entry).getLibraryName();
problemsHolder.registerProblem(message, null, ProjectStructureProblemType.warning(errorId), placeInPS, quickFix);
}
}
}
}
@Override
public List<ProjectStructureElementUsage> getUsagesInElement() {
FlexBCConfigurator configurator = FlexBuildConfigurationsExtension.getInstance().getConfigurator();
final FlexProjectConfigurationEditor editor = configurator.getConfigEditor();
assert editor != null;
final ModulesConfigurator modulesConfigurator = myContext.getModulesConfigurator();
final List<ProjectStructureElementUsage> usages = new ArrayList<>();
for (DependencyEntry dependencyEntry : myBc.getDependencies().getEntries()) {
if (dependencyEntry instanceof SharedLibraryEntry) {
String libraryName = ((SharedLibraryEntry)dependencyEntry).getLibraryName();
String libraryLevel = ((SharedLibraryEntry)dependencyEntry).getLibraryLevel();
final Library library = myContext.getLibrary(libraryName, libraryLevel);
if (library != null) {
usages.add(new UsageInBcDependencies(this, new LibraryProjectStructureElement(myContext, library)) {
@Override
public void removeSourceElement() {
libraryReplaced(library, null);
}
@Override
public void replaceElement(final ProjectStructureElement newElement) {
libraryReplaced(library, ((LibraryProjectStructureElement)newElement).getLibrary());
}
});
}
}
else if (dependencyEntry instanceof BuildConfigurationEntry) {
final BuildConfigurationEntry bcEntry = (BuildConfigurationEntry)dependencyEntry;
Module module = modulesConfigurator.getModule(bcEntry.getModuleName());
if (module != null) {
final ModifiableFlexBuildConfiguration bc =
ContainerUtil.find(editor.getConfigurations(module), configuration -> bcEntry.getBcName().equals(configuration.getName()));
if (bc != null) {
usages.add(new UsageInBcDependencies(this, new BuildConfigurationProjectStructureElement(bc, module, myContext)) {
@Override
public void removeSourceElement() {
// ignore as editor already listens to BC removal
}
@Override
public void replaceElement(final ProjectStructureElement newElement) {
throw new UnsupportedOperationException();
}
});
}
}
bcEntry.findBuildConfiguration();
}
}
Sdk sdk = myBc.getSdk();
if (sdk != null) {
usages.add(new UsageInBcDependencies(this, new SdkProjectStructureElement(myContext, sdk)) {
@Override
public void removeSourceElement() {
myBc.getDependencies().setSdkEntry(null);
}
@Override
public void replaceElement(final ProjectStructureElement newElement) {
throw new UnsupportedOperationException();
}
});
}
return usages;
}
protected void libraryReplaced(@NotNull final Library library, @Nullable final Library replacement) {
}
@Override
public boolean equals(final Object obj) {
return obj instanceof BuildConfigurationProjectStructureElement &&
myModule.equals(((BuildConfigurationProjectStructureElement)obj).myModule) &&
myBc.equals(((BuildConfigurationProjectStructureElement)obj).myBc);
}
@Override
public int hashCode() {
return myModule.hashCode() ^ myBc.hashCode();
}
public StructureConfigurableContext getContext() {
return myContext;
}
public Module getModule() {
return myModule;
}
public FlexBuildConfiguration getBC() {
return myBc;
}
@Override
public String getDescription() {
return myBc.getDescription();
}
}