/* * Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan * * 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.goide.project.migration; import com.goide.GoConstants; import com.goide.project.GoApplicationLibrariesService; import com.goide.project.GoProjectLibrariesService; import com.goide.sdk.GoSdkType; import com.goide.sdk.GoSdkUtil; import com.intellij.conversion.*; import com.intellij.ide.impl.convert.JDomConvertingUtil; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.projectRoots.ProjectJdkTable; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.impl.ProjectRootManagerImpl; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.serialization.JDomSerializationUtil; import java.io.File; import java.io.IOException; import java.util.Collection; public class GoProjectModelConverterProvider extends ConverterProvider { private static final String PROJECT_ROOT_MANAGER = "ProjectRootManager"; protected GoProjectModelConverterProvider() { super("go-project-model"); } @NotNull @Override public String getConversionDescription() { return "Go project model has been changed so project and its modules need to be updated"; } @NotNull @Override public ProjectConverter createConverter(@NotNull ConversionContext context) { return new ProjectConverter() { private final Collection<File> additionalCreatedFiles = ContainerUtil.newArrayList(); private final Collection<File> additionalAffectedFiles = ContainerUtil.newArrayList(); @Nullable @Override public ConversionProcessor<ProjectSettings> createProjectFileConverter() { return new ProjectFileConverter(); } @Nullable @Override public ConversionProcessor<ModuleSettings> createModuleFileConverter() { return new ModuleFileConverter(); } @Nullable @Override public ConversionProcessor<RunManagerSettings> createRunConfigurationsConverter() { return new RunConfigurationsConverter(); } @Override public boolean isConversionNeeded() { Element component = getProjectRootManager(context); return component != null && isGoSdkType(component.getAttributeValue(ProjectRootManagerImpl.PROJECT_JDK_TYPE_ATTR)); } @Override public void preProcessingFinished() throws CannotConvertException { Element component = getProjectRootManager(context); if (component != null) { try { File miscFile = miscFile(context); updateSdkType(miscFile, component); additionalAffectedFiles.add(miscFile); File oldGoSettings = new File(context.getSettingsBaseDir(), "go_settings.xml"); if (oldGoSettings.exists()) { Element oldGoSettingsRoot = rootElement(oldGoSettings); if (isAttachProjectDirToLibraries(oldGoSettingsRoot)) { File librariesConfigFile = new File(context.getSettingsBaseDir(), GoConstants.GO_LIBRARIES_CONFIG_FILE); if (librariesConfigFile.exists()) { //noinspection ResultOfMethodCallIgnored librariesConfigFile.delete(); additionalAffectedFiles.add(librariesConfigFile); } else { additionalCreatedFiles.add(librariesConfigFile); } FileUtil.writeToFile(librariesConfigFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project></project>"); addProjectDirToLibraries(librariesConfigFile, rootElement(librariesConfigFile)); } } //noinspection ResultOfMethodCallIgnored oldGoSettings.delete(); } catch (IOException e) { throw new CannotConvertException("Cannot convert go project", e); } } convertSdks(); } @NotNull @Override public Collection<File> getAdditionalAffectedFiles() { return additionalAffectedFiles; } @NotNull @Override public Collection<File> getCreatedFiles() { return additionalCreatedFiles; } }; } @NotNull private static Element rootElement(@NotNull File file) throws CannotConvertException { return JDomConvertingUtil.loadDocument(file).getRootElement(); } private static class ProjectFileConverter extends ConversionProcessor<ProjectSettings> { @Override public boolean isConversionNeeded(@NotNull ProjectSettings settings) { Element projectRootManager = getProjectRootManager(settings.getRootElement()); return projectRootManager != null && isGoSdkType(projectRootManager.getAttributeValue(ProjectRootManagerImpl.PROJECT_JDK_TYPE_ATTR)); } @Override public void process(@NotNull ProjectSettings settings) throws CannotConvertException { Element projectRootManager = getProjectRootManager(settings.getRootElement()); if (projectRootManager != null) { updateSdkType(settings.getFile(), projectRootManager); } if (isAttachProjectDirToLibraries(settings.getRootElement())) { addProjectDirToLibraries(settings.getFile(), settings.getRootElement()); } convertSdks(); } } private static class ModuleFileConverter extends ConversionProcessor<ModuleSettings> { @Override public boolean isConversionNeeded(@NotNull ModuleSettings settings) { if ("GO_APP_ENGINE_MODULE".equals(settings.getModuleType())) return true; for (Element element : settings.getOrderEntries()) { if (isGoSdkType(element.getAttributeValue("jdkType"))) { return true; } } return false; } @Override public void process(@NotNull ModuleSettings settings) throws CannotConvertException { settings.setModuleType(GoConstants.MODULE_TYPE_ID); for (Element element : settings.getOrderEntries()) { if (isGoSdkType(element.getAttributeValue("jdkType"))) { element.setAttribute("jdkType", GoConstants.SDK_TYPE_ID); } } convertSdks(); } } private static Element getProjectRootManager(@NotNull ConversionContext context) { File miscFile = miscFile(context); try { if (miscFile.exists()) { return getProjectRootManager(rootElement(miscFile)); } } catch (CannotConvertException e) { return null; } return null; } @Nullable private static Element getProjectRootManager(@Nullable Element rootElement) { return rootElement != null ? JDomSerializationUtil.findComponent(rootElement, PROJECT_ROOT_MANAGER) : null; } private static void updateSdkType(@NotNull File file, @NotNull Element projectRootManager) throws CannotConvertException { projectRootManager.setAttribute(ProjectRootManagerImpl.PROJECT_JDK_TYPE_ATTR, GoConstants.SDK_TYPE_ID); saveFile(file, projectRootManager, "Cannot save sdk type changing"); } @NotNull private static File miscFile(@NotNull ConversionContext context) { return new File(context.getSettingsBaseDir(), "misc.xml"); } private static void addProjectDirToLibraries(@NotNull File file, @NotNull Element rootElement) throws CannotConvertException { GoProjectLibrariesService librariesService = new GoProjectLibrariesService(); librariesService.setLibraryRootUrls("file://$PROJECT_DIR$"); Element componentElement = JDomSerializationUtil.findOrCreateComponentElement(rootElement, GoConstants.GO_LIBRARIES_SERVICE_NAME); XmlSerializer.serializeInto(librariesService.getState(), componentElement); saveFile(file, rootElement, "Cannot save libraries settings"); } private static boolean isAttachProjectDirToLibraries(Element rootElement) { Element goProjectSettings = JDomSerializationUtil.findComponent(rootElement, "GoProjectSettings"); if (goProjectSettings != null) { for (Element option : goProjectSettings.getChildren("option")) { if ("prependGoPath".equals(option.getAttributeValue("name"))) { goProjectSettings.detach(); return "true".equalsIgnoreCase(option.getAttributeValue("value")); } } goProjectSettings.detach(); } return false; } private static boolean isGoSdkType(String sdkTypeName) { return "Google Go SDK".equals(sdkTypeName) || "Google Go App Engine SDK".equals(sdkTypeName); } private static void saveFile(@NotNull File file, @NotNull Element rootElement, String errorMessage) throws CannotConvertException { try { JDOMUtil.writeDocument(rootElement.getDocument(), file, SystemProperties.getLineSeparator()); } catch (IOException e) { throw new CannotConvertException(errorMessage, e); } } private static void convertSdks() { ProjectJdkTable sdkTable = ProjectJdkTable.getInstance(); Collection<String> globalGoPathUrls = ContainerUtil.newLinkedHashSet(); Collection<Sdk> sdksToDelete = ContainerUtil.newArrayList(); Collection<Sdk> sdksToAdd = ContainerUtil.newArrayList(); GoSdkType sdkType = GoSdkType.getInstance(); for (Sdk sdk : sdkTable.getAllJdks()) { String sdkTypeName = sdk.getSdkType().getName(); if (isGoSdkType(sdkTypeName)) { sdksToDelete.add(sdk); String sdkHome = sdkType.adjustSelectedSdkHome(sdk.getHomePath()); if (sdkType.isValidSdkHome(sdkHome)) { ProjectJdkImpl newSdk = new ProjectJdkImpl(sdk.getName(), sdkType, sdkHome, sdkType.getVersionString(sdkHome)); sdkType.setupSdkPaths(newSdk); sdksToAdd.add(newSdk); for (String classesRoot : sdk.getRootProvider().getUrls(OrderRootType.CLASSES)) { if (!classesRoot.equals(sdk.getHomePath())) { globalGoPathUrls.add(classesRoot); } } } } } for (VirtualFile file : GoSdkUtil.getGoPathsRootsFromEnvironment()) { globalGoPathUrls.remove(file.getUrl()); } AccessToken l = WriteAction.start(); try { for (Sdk sdk : sdksToDelete) { sdkTable.removeJdk(sdk); } for (Sdk sdk : sdksToAdd) { sdkTable.addJdk(sdk); } globalGoPathUrls.addAll(GoApplicationLibrariesService.getInstance().getLibraryRootUrls()); GoApplicationLibrariesService.getInstance().setLibraryRootUrls(globalGoPathUrls); } finally { l.finish(); } } private static class RunConfigurationsConverter extends ConversionProcessor<RunManagerSettings> { @Override public boolean isConversionNeeded(@NotNull RunManagerSettings settings) { for (Element element : settings.getRunConfigurations()) { String confType = element.getAttributeValue("type"); if ("GaeLocalAppEngineServer".equals(confType) || "GoTestConfiguration".equals(confType)) { return true; } } return false; } @Override public void process(@NotNull RunManagerSettings settings) throws CannotConvertException { for (Element element : settings.getRunConfigurations()) { String confType = element.getAttributeValue("type"); if ("GaeLocalAppEngineServer".equals(confType) || "GoTestConfiguration".equals(confType)) { element.detach(); } } } } }