/* * 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.backgroundTaskByVfsChange; import com.intellij.application.options.ReplacePathToMacroMap; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.backgroundTaskByVfsChange.ui.BackgroundTaskByVfsChangeManageDialog; import java.util.ArrayList; import java.util.List; /** * @author VISTALL * @since 22:50/06.10.13 */ @State( name = "BackgroundTaskByVfsChangeManager", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) public class BackgroundTaskByVfsChangeManagerImpl extends BackgroundTaskByVfsChangeManager implements PersistentStateComponent<Element>, Disposable { private static final Key<Boolean> PROCESSING_BACKGROUND_TASK = Key.create("processing.background.task"); @NotNull private final Project myProject; private List<BackgroundTaskByVfsChangeTaskImpl> myTasks = new ArrayList<>(); private Listener myListener; private final VirtualFileAdapter myVirtualFileListener; public BackgroundTaskByVfsChangeManagerImpl(@NotNull Project project) { myProject = project; myListener = project.getMessageBus().syncPublisher(TOPIC); myVirtualFileListener = new VirtualFileAdapter() { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { runTasks(event.getFile()); } }; VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileListener); } @Override public void openManageDialog(@NotNull VirtualFile virtualFile) { new BackgroundTaskByVfsChangeManageDialog(myProject, virtualFile).show(); } @NotNull @Override public List<BackgroundTaskByVfsChangeTask> findTasks(@NotNull VirtualFile virtualFile) { List<BackgroundTaskByVfsChangeTask> list = new ArrayList<>(); for (BackgroundTaskByVfsChangeTaskImpl task : myTasks) { VirtualFile file = task.getVirtualFilePointer().getFile(); if (file == null) { continue; } if (file.equals(virtualFile)) { list.add(task); } } return list; } @NotNull @Override public List<BackgroundTaskByVfsChangeTask> findEnabledTasks(@NotNull VirtualFile virtualFile) { List<BackgroundTaskByVfsChangeTask> list = new ArrayList<>(); for (BackgroundTaskByVfsChangeTaskImpl task : myTasks) { if(!task.isEnabled()) { continue; } VirtualFile file = task.getVirtualFilePointer().getFile(); if (file == null) { continue; } if (file.equals(virtualFile)) { list.add(task); } } return list; } @NotNull @Override public BackgroundTaskByVfsChangeTask[] getTasks() { return myTasks.toArray(new BackgroundTaskByVfsChangeTask[myTasks.size()]); } @Override public boolean cancelTask(@NotNull BackgroundTaskByVfsChangeTask task) { assert task instanceof BackgroundTaskByVfsChangeTaskImpl; boolean remove = myTasks.remove(task); if (remove) { myListener.taskCanceled(task); } return remove; } @Override public void registerTask(@NotNull BackgroundTaskByVfsChangeTask task) { BackgroundTaskByVfsChangeTaskImpl implTask = (BackgroundTaskByVfsChangeTaskImpl)task; myTasks.add(implTask); myListener.taskAdded(task); } @Override public void runTasks(@NotNull final VirtualFile virtualFile) { Boolean processed = virtualFile.getUserData(PROCESSING_BACKGROUND_TASK); if(processed == Boolean.TRUE) { return; } final List<BackgroundTaskByVfsChangeTask> tasks = findEnabledTasks(virtualFile); if (tasks.isEmpty()) { return; } Task.Backgroundable backgroundTask = new Task.Backgroundable(myProject, "Processing: " + virtualFile.getName()) { @Override public void run(@NotNull ProgressIndicator indicator) { virtualFile.putUserData(PROCESSING_BACKGROUND_TASK, Boolean.TRUE); call(indicator, virtualFile, tasks, 0); } }; backgroundTask.queue(); } public void call(final ProgressIndicator indicator, final VirtualFile file, final List<BackgroundTaskByVfsChangeTask> tasks, final int index) { if(index == tasks.size()) { file.putUserData(PROCESSING_BACKGROUND_TASK, null); return; } BackgroundTaskByVfsChangeTaskImpl task = (BackgroundTaskByVfsChangeTaskImpl)tasks.get(index); ActionCallback callback = new ActionCallback(); callback.doWhenProcessed(() -> call(indicator, file, tasks, index + 1)); indicator.setText2("Task: " + task.getName()); task.run(callback); } @Nullable @Override public Element getState() { Element element = new Element("state"); for (BackgroundTaskByVfsChangeTaskImpl task : myTasks) { Element taskElement = new Element("task"); element.addContent(taskElement); taskElement.setAttribute("url", task.getVirtualFilePointer().getUrl()); taskElement.setAttribute("provider-name", task.getProviderName()); taskElement.setAttribute("name", task.getName()); taskElement.setAttribute("enabled", String.valueOf(task.isEnabled())); Element serialize = XmlSerializer.serialize(task.getParameters()); taskElement.addContent(serialize); ExpandMacroToPathMap expandMacroToPathMap = task.createExpandMacroToPathMap(); expandMacroToPathMap.substitute(serialize, false, true); } return element; } @Override public void loadState(Element state) { for (Element element : state.getChildren("task")) { String url = element.getAttributeValue("url"); String name = element.getAttributeValue("name"); String providerName = element.getAttributeValue("provider-name"); boolean enabled = Boolean.valueOf(element.getAttributeValue("enabled")); VirtualFilePointer virtualFilePointer = VirtualFilePointerManager.getInstance().create(url, this, null); BackgroundTaskByVfsParametersImpl parameters = new BackgroundTaskByVfsParametersImpl(myProject); BackgroundTaskByVfsChangeTaskImpl task = new BackgroundTaskByVfsChangeTaskImpl(myProject, virtualFilePointer, providerName, name, parameters, this); task.setEnabled(enabled); Element parametersElement = element.getChild("parameters"); if (parametersElement != null) { ReplacePathToMacroMap replaceMacroToPathMap = task.createReplaceMacroToPathMap(); replaceMacroToPathMap.substitute(parametersElement, false, true); XmlSerializer.deserializeInto(parameters, parametersElement); } registerTask(task); } } @Override public void dispose() { VirtualFileManager.getInstance().removeVirtualFileListener(myVirtualFileListener); myTasks.clear(); } public void taskChanged(BackgroundTaskByVfsChangeTaskImpl backgroundTaskByVfsChangeTask) { myListener.taskChanged(backgroundTaskByVfsChangeTask); } }