blob: 8f23eeb8d0a8e37b4d08e7d6adb3ebb9d843e332 [file] [log] [blame]
/*
* 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);
}
}
}