/* * Copyright 2003-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 jetbrains.mps.idea.core.library; import com.intellij.ide.util.ChooseElementsDialog; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.DummyLibraryProperties; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryType; import com.intellij.openapi.roots.libraries.NewLibraryConfiguration; import com.intellij.openapi.roots.libraries.PersistentLibraryKind; import com.intellij.openapi.roots.libraries.ui.AttachRootButtonDescriptor; import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent; import com.intellij.openapi.roots.libraries.ui.LibraryPropertiesEditor; import com.intellij.openapi.roots.libraries.ui.LibraryRootsComponentDescriptor; import com.intellij.openapi.roots.libraries.ui.OrderRoot; import com.intellij.openapi.roots.libraries.ui.OrderRootTypePresentation; import com.intellij.openapi.roots.libraries.ui.RootDetector; import com.intellij.openapi.roots.ui.configuration.FacetsProvider; import com.intellij.openapi.roots.ui.configuration.libraryEditor.DefaultLibraryRootsComponentDescriptor; import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.ide.vfs.VirtualFileUtils; import jetbrains.mps.idea.core.MPSBundle; import jetbrains.mps.idea.core.facet.MPSFacetType; import jetbrains.mps.idea.core.icons.MPSIcons; import jetbrains.mps.idea.core.project.SolutionIdea; import jetbrains.mps.project.AbstractModule; import jetbrains.mps.project.SModuleOperations; import jetbrains.mps.project.Solution; import jetbrains.mps.smodel.ModuleRepositoryFacade; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.openapi.module.SRepository; import javax.swing.Icon; import javax.swing.JComponent; import java.awt.Component; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public class ModuleLibraryType extends LibraryType<DummyLibraryProperties> { public ModuleLibraryType() { super(MpsModuleLibraryKindContainer.MPS_MODULE_LIBRARY_KIND); } @Nullable public static VirtualFile getJarFile(String path) { VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, path)); if (vFile == null || vFile.isDirectory() || vFile.getFileType() != FileTypes.ARCHIVE) { return null; } return JarFileSystem.getInstance().findFileByPath(vFile.getPath() + JarFileSystem.JAR_SEPARATOR); } public static Set<VirtualFile> getModuleJars(AbstractModule usedModule) { Set<VirtualFile> stubFiles = new HashSet<VirtualFile>(); for (String stubPath : SModuleOperations.getJavaFacet(usedModule).getClassPath()) { VirtualFile jarFile = getJarFile(stubPath); if (jarFile != null) { stubFiles.add(jarFile); } } return stubFiles; } @Override public String getCreateActionName() { return MPSBundle.message("library.mps.solutions"); } @Override public NewLibraryConfiguration createNewLibrary(@NotNull JComponent parentComponent, @Nullable VirtualFile contextDirectory, @NotNull final Project project) { List<SModuleReference> availableSolutions = calculateVisibleModules(ProjectHelper.getProjectRepository(project), Collections.<VirtualFile>emptySet()); ChooseElementsDialog<SModuleReference> chooser = new SModuleReferenceChooserDialog(project, availableSolutions); chooser.show(); List<SModuleReference> chosenElements = chooser.getChosenElements(); if (chosenElements.isEmpty()) { return null; } String name = ModuleLibrariesUtil.LIBRARY_PREFIX + chosenElements.get(0).getModuleName(); if (chosenElements.size() > 1) { name += "..."; } final Set<OrderRoot> roots = createRootsFor(chosenElements); return new NewLibraryConfiguration(name, this, new DummyLibraryProperties()) { @Override public void addRoots(@NotNull LibraryEditor editor) { editor.addRoots(roots); } }; } private Set<OrderRoot> createRootsFor(List<SModuleReference> chosenElements) { final Set<OrderRoot> roots = new LinkedHashSet<OrderRoot>(); for (SModuleReference moduleReference : chosenElements) { AbstractModule module = (AbstractModule) ModuleRepositoryFacade.getInstance().getModule(moduleReference); roots.add(new OrderRoot(VirtualFileUtils.getOrCreateVirtualFile(module.getDescriptorFile()), ModuleXmlRootDetector.MPS_MODULE_XML, false)); for (VirtualFile virtualFile : getModuleJars(module)) { roots.add(new OrderRoot(virtualFile, OrderRootType.CLASSES, false)); } } return roots; } @Override public LibraryPropertiesEditor createPropertiesEditor(@NotNull LibraryEditorComponent editorComponent) { return null; } @Override public Icon getIcon() { return MPSIcons.MPS_ICON; } @Override public DummyLibraryProperties detect(@NotNull List<VirtualFile> classesRoots) { return super.detect(classesRoots); } @Override public boolean isSuitableModule(@NotNull Module module, @NotNull FacetsProvider facetsProvider) { return !facetsProvider.getFacetsByType(module, MPSFacetType.ID).isEmpty(); } @Override public LibraryRootsComponentDescriptor createLibraryRootsComponentDescriptor() { return new MyLibraryRootsComponentDescriptor(); } public static boolean isModuleLibrary(Library l) { if (l instanceof LibraryEx) { PersistentLibraryKind<?> kind = ((LibraryEx) l).getKind(); return kind != null && MpsModuleLibraryKindContainer.MPS_MODULE_LIBRARY_KIND.getKindId().equals(kind.getKindId()); } return false; } public static ModuleLibraryType getInstance() { return LibraryType.EP_NAME.findExtension(ModuleLibraryType.class); } private static class SModuleReferenceChooserDialog extends ChooseElementsDialog<SModuleReference> { public SModuleReferenceChooserDialog(Project project, List<SModuleReference> availableSolutions) { super(project, availableSolutions, MPSBundle.message("used.modules.chooser.title"), null); } private SModuleReferenceChooserDialog(Component parent, List<SModuleReference> availableSolutions) { super(parent, availableSolutions, MPSBundle.message("used.modules.chooser.title")); } @Override protected String getItemText(SModuleReference item) { return item.getModuleName(); } @Override protected Icon getItemIcon(SModuleReference item) { if (ModuleRepositoryFacade.getInstance().getModule(item) instanceof Solution) { return MPSIcons.SOLUTION_ICON; } else { return MPSIcons.LANGUAGE_ICON; } } } private static class MyLibraryRootsComponentDescriptor extends DefaultLibraryRootsComponentDescriptor { @Override public OrderRootTypePresentation getRootTypePresentation(@NotNull OrderRootType type) { if (type == ModuleXmlRootDetector.MPS_MODULE_XML) { return ModuleXmlRootDetector.getPresentation(); } return null; } @Override public OrderRootType[] getRootTypes() { ArrayList<OrderRootType> types = new ArrayList<OrderRootType>(); types.addAll(Arrays.asList(super.getRootTypes())); types.add(ModuleXmlRootDetector.MPS_MODULE_XML); return types.toArray(new OrderRootType[types.size()]); } @NotNull @Override public List<? extends RootDetector> getRootDetectors() { List<RootDetector> detectors = new ArrayList<RootDetector>(); detectors.addAll(super.getRootDetectors()); detectors.add(ModuleXmlRootDetector.getInstance()); return detectors; } @NotNull @Override public FileChooserDescriptor createAttachFilesChooserDescriptor(@Nullable String libraryName) { // same as super apart from the constructor invocation parameters FileChooserDescriptor descriptor = new FileChooserDescriptor(false, false, true, false, true, true); descriptor.setTitle(StringUtil.isEmpty(libraryName) ? ProjectBundle.message("library.attach.files.action") : ProjectBundle.message("library.attach.files.to.library.action", libraryName)); descriptor.setDescription(ProjectBundle.message("library.attach.files.description")); return descriptor; } @NotNull @Override public List<? extends AttachRootButtonDescriptor> createAttachButtons() { return Arrays.asList(new AttachRootButtonDescriptor(ModuleXmlRootDetector.MPS_MODULE_XML, MPSBundle.message("library.attach.mps.solution")) { @Override public VirtualFile[] selectFiles(@NotNull JComponent parent, @Nullable VirtualFile initialSelection, @Nullable final Module contextModule, @NotNull final LibraryEditor libraryEditor) { SRepository repository = ProjectHelper.getProjectRepository(contextModule.getProject()); List<SModuleReference> visibleModules = calculateVisibleModules(repository, new HashSet<VirtualFile>(Arrays.asList(libraryEditor.getFiles(ModuleXmlRootDetector.MPS_MODULE_XML)))); ChooseElementsDialog<SModuleReference> chooser = new SModuleReferenceChooserDialog(parent, visibleModules); chooser.show(); final List<SModuleReference> chosenElements = chooser.getChosenElements(); final Set<VirtualFile> addedDescriptors = new LinkedHashSet<VirtualFile>(); final Set<VirtualFile> addedJars = new LinkedHashSet<VirtualFile>(); repository.getModelAccess().runReadAction(new Runnable() { @Override public void run() { for (SModuleReference module : chosenElements) { AbstractModule chosenModule = (AbstractModule) ModuleRepositoryFacade.getInstance().getModule(module); addedDescriptors.add(VirtualFileUtils.getOrCreateVirtualFile(chosenModule.getDescriptorFile())); for (VirtualFile virtualFile : getModuleJars(chosenModule)) { addedJars.add(virtualFile); } } } }); // that's a hack // I want to add 2 different root types here: classes and module xml-s for (VirtualFile classesJar : addedJars) { libraryEditor.addRoot(classesJar, OrderRootType.CLASSES); } return addedDescriptors.toArray(new VirtualFile[addedDescriptors.size()]); } }); } } private static List<SModuleReference> calculateVisibleModules(SRepository repository, final Set<VirtualFile> excluded) { final List<SModuleReference> availableSolutions = new ArrayList<SModuleReference>(); final List<SModuleReference> availableLanguages = new ArrayList<SModuleReference>(); repository.getModelAccess().runReadAction(new Runnable() { @Override public void run() { for (SModule module : new ModuleRepositoryFacade(repository).getAllModules(SModule.class)) { if (module instanceof SolutionIdea || ((AbstractModule) module).getDescriptorFile() == null) { continue; } if (excluded.contains(VirtualFileUtils.getOrCreateVirtualFile(((AbstractModule) module).getDescriptorFile()))) { // skip solutions that are already in a lib continue; } if (module instanceof Solution) { availableSolutions.add(module.getModuleReference()); } else { availableLanguages.add(module.getModuleReference()); } } } }); Comparator<SModuleReference> moduleComparator = new Comparator<SModuleReference>() { @Override public int compare(SModuleReference o1, SModuleReference o2) { return o1.getModuleName().compareTo(o2.getModuleName()); } }; Collections.sort(availableSolutions, moduleComparator); Collections.sort(availableLanguages, moduleComparator); List<SModuleReference> result = new ArrayList<SModuleReference>(); result.addAll(availableSolutions); result.addAll(availableLanguages); return result; } }