| /* |
| * Copyright 2000-2014 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 org.jetbrains.idea.svn; |
| |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.command.CommandEvent; |
| import com.intellij.openapi.command.CommandListener; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectLocator; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.vfs.LocalFileOperationsHandler; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.ThrowableConsumer; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.IOException; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| public class SvnFileSystemListenerWrapper { |
| private final LocalFileOperationsHandler myProxy; |
| private final CommandListener myListener; |
| |
| public SvnFileSystemListenerWrapper(final SvnFileSystemListener delegate) { |
| final MyCommandListener listener = new MyCommandListener(delegate); |
| myListener = listener; |
| final MyStorage storage = new MyStorage(listener); |
| myProxy = (LocalFileOperationsHandler) Proxy.newProxyInstance(LocalFileOperationsHandler.class.getClassLoader(), |
| new Class<?>[]{LocalFileOperationsHandler.class}, new MyInvoker(storage, delegate)); |
| } |
| |
| public void registerSelf() { |
| LocalFileSystem.getInstance().registerAuxiliaryFileOperationsHandler(myProxy); |
| CommandProcessor.getInstance().addCommandListener(myListener); |
| } |
| |
| public void unregisterSelf() { |
| LocalFileSystem.getInstance().unregisterAuxiliaryFileOperationsHandler(myProxy); |
| CommandProcessor.getInstance().removeCommandListener(myListener); |
| } |
| |
| private static class MyCommandListener implements CommandListener, MyMarker { |
| private volatile boolean myInCommand; |
| private final SvnFileSystemListener myDelegate; |
| |
| public MyCommandListener(final SvnFileSystemListener delegate) { |
| myDelegate = delegate; |
| } |
| |
| public void start(final Project project) { |
| if (!myInCommand && project != null) { |
| myDelegate.commandStarted(project); |
| } |
| } |
| |
| public void finish(final Project project) { |
| if (! myInCommand && project != null) { |
| myDelegate.commandFinished(project); |
| } |
| } |
| |
| public void commandStarted(CommandEvent event) { |
| myInCommand = true; |
| myDelegate.commandStarted(event); |
| } |
| |
| public void beforeCommandFinished(CommandEvent event) { |
| myDelegate.beforeCommandFinished(event); |
| } |
| |
| public void commandFinished(CommandEvent event) { |
| myInCommand = false; |
| myDelegate.commandFinished(event); |
| } |
| |
| public void undoTransparentActionStarted() { |
| myDelegate.undoTransparentActionStarted(); |
| } |
| |
| public void undoTransparentActionFinished() { |
| myDelegate.undoTransparentActionFinished(); |
| } |
| } |
| |
| private interface MyMarker { |
| void start(final Project project); |
| void finish(final Project project); |
| } |
| |
| private static class MyStorage implements InvocationHandler { |
| private final MyMarker myMarker; |
| private final Map<Project, Pair<String, Object[]>> myStarted; |
| |
| private MyStorage(final MyMarker marker) { |
| myMarker = marker; |
| myStarted = new HashMap<Project, Pair<String, Object[]>>(); |
| } |
| |
| @Nullable |
| private static Project getProject(Object[] args) { |
| for (Object arg : args) { |
| if (arg instanceof VirtualFile) { |
| return ProjectLocator.getInstance().guessProjectForFile((VirtualFile) arg); |
| } |
| } |
| return null; |
| } |
| |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| final Project project = getProject(args); |
| if (project != null) { |
| final Pair<String, Object[]> pair = myStarted.get(project); |
| if (pair != null && method.getName().equals(pair.getFirst()) && Arrays.equals(args, pair.getSecond())) { |
| myMarker.finish(project); |
| } |
| } |
| //dont return null for auto unboxing to not face NPE |
| return "boolean".equals(method.getReturnType().getName()) ? Boolean.TRUE : null; |
| } |
| |
| private void register(final Method method, final Object[] args) { |
| final Object[] newArr = new Object[args.length]; |
| System.arraycopy(args, 0, newArr, 0, args.length); |
| final Project project = getProject(args); |
| if (project != null) { |
| myMarker.start(project); |
| myStarted.put(project, Pair.create(method.getName(), newArr)); |
| Disposer.register(project, new Disposable() { |
| public void dispose() { |
| myStarted.remove(project); |
| } |
| }); |
| } |
| } |
| } |
| |
| private static class MyInvoker implements InvocationHandler { |
| private final Object myDelegate; |
| private final MyStorage myParent; |
| private final LocalFileOperationsHandler myParentProxy; |
| |
| private MyInvoker(final MyStorage parent, Object delegate) { |
| myParent = parent; |
| myDelegate = delegate; |
| myParentProxy = (LocalFileOperationsHandler) Proxy.newProxyInstance(LocalFileOperationsHandler.class.getClassLoader(), |
| new Class<?>[]{LocalFileOperationsHandler.class}, myParent); |
| } |
| |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| if ("afterDone".equals(method.getName()) && args.length == 1) { |
| ((ThrowableConsumer<LocalFileOperationsHandler, IOException>)args[0]).consume(myParentProxy); |
| return null; |
| } |
| |
| if (LocalFileOperationsHandler.class.equals(method.getDeclaringClass())) { |
| myParent.register(method, args); |
| } |
| if ("equals".equals(method.getName())) { |
| return args[0].equals(this); |
| } |
| else if ("hashCode".equals(method.getName())) { |
| return 1; |
| } |
| return method.invoke(myDelegate, args); |
| } |
| } |
| } |