/*
* Copyright 2013-2016 consulo.io
*
* 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 consulo.unity3d;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.event.HyperlinkEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.PathUtil;
import consulo.annotations.RequiredReadAction;
import consulo.annotations.RequiredWriteAction;
import consulo.dotnet.dll.DotNetModuleFileType;
import consulo.roots.ModifiableModuleRootLayer;
import consulo.roots.ModuleRootLayer;
import consulo.roots.types.BinariesOrderRootType;
import consulo.unity3d.bundle.Unity3dDefineByVersion;
import consulo.unity3d.module.Unity3dModuleExtensionUtil;
import consulo.unity3d.module.Unity3dRootModuleExtension;
import consulo.unity3d.projectImport.Unity3dProjectUtil;
import consulo.vfs.util.ArchiveVfsUtil;
/**
* @author VISTALL
* @since 26-Jul-16
*/
public class UnityPluginFileValidator extends AbstractProjectComponent
{
private static final Logger LOGGER = Logger.getInstance(UnityPluginFileValidator.class);
private static final String ourPath = "Assets/Editor/Plugins";
private static final NotificationGroup ourGroup = new NotificationGroup("consulo.unity", NotificationDisplayType.STICKY_BALLOON, true);
public UnityPluginFileValidator(Project project)
{
super(project);
}
@Override
public void projectOpened()
{
runValidation(myProject);
}
public static void runValidation(@NotNull final Project project)
{
DumbService.getInstance(project).runWhenSmart(new Runnable()
{
@Override
@RequiredReadAction
public void run()
{
notifyAboutPluginFile(project);
}
});
}
@RequiredReadAction
private static void notifyAboutPluginFile(@NotNull final Project project)
{
Unity3dRootModuleExtension moduleExtension = Unity3dModuleExtensionUtil.getRootModuleExtension(project);
if(moduleExtension == null)
{
return;
}
Unity3dDefineByVersion unity3dDefineByVersion = Unity3dProjectUtil.getUnity3dDefineByVersion(moduleExtension.getSdk());
final String pluginFileName = unity3dDefineByVersion.getPluginFileName();
if(pluginFileName == null)
{
return;
}
File pluginPath = PluginManager.getPluginPath(UnityPluginFileValidator.class);
final File unityPluginFile = new File(pluginPath, "UnityEditorConsuloPlugin/" + pluginFileName);
if(!unityPluginFile.exists())
{
return;
}
VirtualFile baseDir = project.getBaseDir();
if(baseDir == null)
{
return;
}
VirtualFile targetFile = null;
VirtualFile fileByRelativePath = baseDir.findFileByRelativePath(ourPath);
if(fileByRelativePath != null)
{
VirtualFile[] children = fileByRelativePath.getChildren();
for(VirtualFile child : children)
{
CharSequence nameSequence = child.getNameSequence();
if(StringUtil.startsWith(nameSequence, "UnityEditorConsuloPlugin") && child.getFileType() == DotNetModuleFileType.INSTANCE)
{
targetFile = child;
break;
}
}
}
if(targetFile == null)
{
showNotify(project, pluginFileName, unityPluginFile, "Consulo plugin for UnityEditor is missing<br><a href=\"#\">Install</a>", null);
}
else
{
final File file = VfsUtil.virtualToIoFile(targetFile);
if(!file.exists())
{
return;
}
String title = null;
if(!pluginFileName.equals(targetFile.getName()))
{
title = "Outdated Consulo plugin for UnityEditor<br><a href=\"#\">Update</a>";
}
else if(file.lastModified() < unityPluginFile.lastModified())
{
title = "New version of Consulo plugin for UnityEditor is available<br><a href=\"#\">Update</a>";
}
if(title != null)
{
final VirtualFile finalTargetFile = targetFile;
showNotify(project, pluginFileName, unityPluginFile, title, new Runnable()
{
@Override
public void run()
{
try
{
finalTargetFile.delete(null);
}
catch(IOException e)
{
//
}
}
});
}
}
}
private static void showNotify(final Project project, final String pluginFileName, final File unityPluginFile, @NotNull String title, @Nullable final Runnable beforeTask)
{
Notification notification = new Notification(ourGroup.getDisplayId(), "Unity3D Plugin", title, NotificationType.INFORMATION);
notification.setListener(new NotificationListener()
{
@Override
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent hyperlinkEvent)
{
notification.hideBalloon();
runTask(project, pluginFileName, unityPluginFile, beforeTask);
}
});
notification.notify(project);
}
private static void runTask(@NotNull final Project project, @NotNull final String pluginFileName, @NotNull final File unityPluginFile, @Nullable final Runnable otherTask)
{
new Task.Backgroundable(project, "Installing plugin", false)
{
@Override
public void run(@NotNull ProgressIndicator progressIndicator)
{
if(otherTask != null)
{
WriteCommandAction.runWriteCommandAction(project, otherTask);
}
final Ref<VirtualFile> fileRef = Ref.create();
File targetFile = new File(project.getBasePath(), ourPath + "/" + pluginFileName);
try
{
FileUtil.copy(unityPluginFile, targetFile);
VirtualFile value = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(targetFile);
VirtualFile archiveRootForLocalFile = value == null ? null : ArchiveVfsUtil.getArchiveRootForLocalFile(value);
fileRef.set(archiveRootForLocalFile);
}
catch(IOException e)
{
LOGGER.error(e);
}
// if target file is not found, no need change module roots
final VirtualFile targetArchiveFile = fileRef.get();
if(targetArchiveFile == null)
{
return;
}
final List<ModifiableRootModel> list = new ArrayList<ModifiableRootModel>(2);
ApplicationManager.getApplication().runReadAction(new Runnable()
{
@Override
public void run()
{
Unity3dRootModuleExtension unity3dRootModuleExtension = Unity3dModuleExtensionUtil.getRootModuleExtension(project);
if(unity3dRootModuleExtension == null)
{
return;
}
ModuleManager moduleManager = ModuleManager.getInstance(project);
Module[] modules = moduleManager.getModules();
for(Module module : modules)
{
String name = module.getName();
if(name.startsWith("Assembly") && name.endsWith("Editor"))
{
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
ModifiableRootModel modifiableModel = moduleRootManager.getModifiableModel();
for(ModuleRootLayer layer : modifiableModel.getLayers().values())
{
LibraryTable moduleLibraryTable = ((ModifiableModuleRootLayer) layer).getModuleLibraryTable();
for(Library library : moduleLibraryTable.getLibraries())
{
String[] files = library.getUrls(BinariesOrderRootType.getInstance());
for(String url : files)
{
String localPath = PathUtil.getFileName(url);
if(StringUtil.startsWith(localPath, "UnityEditorConsuloPlugin"))
{
moduleLibraryTable.removeLibrary(library);
break;
}
}
}
Library library = moduleLibraryTable.createLibrary();
Library.ModifiableModel libraryModifiableModel = library.getModifiableModel();
libraryModifiableModel.addRoot(targetArchiveFile, BinariesOrderRootType.getInstance());
libraryModifiableModel.commit();
}
list.add(modifiableModel);
}
}
}
});
WriteCommandAction.runWriteCommandAction(project, new Runnable()
{
@Override
@RequiredWriteAction
public void run()
{
for(ModifiableRootModel modifiableRootModel : list)
{
modifiableRootModel.commit();
}
}
});
}
}.queue();
}
}