2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.jetbrains
.idea
.svn
;
18 import com
.intellij
.openapi
.Disposable
;
19 import com
.intellij
.openapi
.command
.CommandEvent
;
20 import com
.intellij
.openapi
.command
.CommandListener
;
21 import com
.intellij
.openapi
.command
.CommandProcessor
;
22 import com
.intellij
.openapi
.project
.Project
;
23 import com
.intellij
.openapi
.project
.ProjectLocator
;
24 import com
.intellij
.openapi
.util
.Disposer
;
25 import com
.intellij
.openapi
.util
.Pair
;
26 import com
.intellij
.openapi
.vfs
.LocalFileOperationsHandler
;
27 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
28 import com
.intellij
.openapi
.vfs
.VirtualFile
;
29 import com
.intellij
.util
.ThrowableConsumer
;
30 import org
.jetbrains
.annotations
.Nullable
;
32 import java
.io
.IOException
;
33 import java
.lang
.reflect
.InvocationHandler
;
34 import java
.lang
.reflect
.Method
;
35 import java
.lang
.reflect
.Proxy
;
36 import java
.util
.Arrays
;
37 import java
.util
.HashMap
;
40 public class SvnFileSystemListenerWrapper
{
41 private final LocalFileOperationsHandler myProxy
;
42 private final CommandListener myListener
;
44 public SvnFileSystemListenerWrapper(final SvnFileSystemListener delegate
) {
45 final MyCommandListener listener
= new MyCommandListener(delegate
);
46 myListener
= listener
;
47 final MyStorage storage
= new MyStorage(listener
);
48 myProxy
= (LocalFileOperationsHandler
) Proxy
.newProxyInstance(LocalFileOperationsHandler
.class.getClassLoader(),
49 new Class
<?
>[]{LocalFileOperationsHandler
.class}, new MyInvoker(storage
, delegate
));
52 public void registerSelf() {
53 LocalFileSystem
.getInstance().registerAuxiliaryFileOperationsHandler(myProxy
);
54 CommandProcessor
.getInstance().addCommandListener(myListener
);
57 public void unregisterSelf() {
58 LocalFileSystem
.getInstance().unregisterAuxiliaryFileOperationsHandler(myProxy
);
59 CommandProcessor
.getInstance().removeCommandListener(myListener
);
62 private static class MyCommandListener
implements CommandListener
, MyMarker
{
63 private volatile boolean myInCommand
;
64 private final SvnFileSystemListener myDelegate
;
66 public MyCommandListener(final SvnFileSystemListener delegate
) {
67 myDelegate
= delegate
;
70 public void start(final Project project
) {
71 if (!myInCommand
&& project
!= null) {
72 myDelegate
.commandStarted(project
);
76 public void finish(final Project project
) {
77 if (! myInCommand
&& project
!= null) {
78 myDelegate
.commandFinished(project
);
82 public void commandStarted(CommandEvent event
) {
84 myDelegate
.commandStarted(event
);
87 public void beforeCommandFinished(CommandEvent event
) {
88 myDelegate
.beforeCommandFinished(event
);
91 public void commandFinished(CommandEvent event
) {
93 myDelegate
.commandFinished(event
);
96 public void undoTransparentActionStarted() {
97 myDelegate
.undoTransparentActionStarted();
100 public void undoTransparentActionFinished() {
101 myDelegate
.undoTransparentActionFinished();
105 private interface MyMarker
{
106 void start(final Project project
);
107 void finish(final Project project
);
110 private static class MyStorage
implements InvocationHandler
{
111 private final MyMarker myMarker
;
112 private final Map
<Project
, Pair
<String
, Object
[]>> myStarted
;
114 private MyStorage(final MyMarker marker
) {
116 myStarted
= new HashMap
<Project
, Pair
<String
, Object
[]>>();
120 private static Project
getProject(Object
[] args
) {
121 for (Object arg
: args
) {
122 if (arg
instanceof VirtualFile
) {
123 return ProjectLocator
.getInstance().guessProjectForFile((VirtualFile
) arg
);
129 public Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{
130 final Project project
= getProject(args
);
131 if (project
!= null) {
132 final Pair
<String
, Object
[]> pair
= myStarted
.get(project
);
133 if (pair
!= null && method
.getName().equals(pair
.getFirst()) && Arrays
.equals(args
, pair
.getSecond())) {
134 myMarker
.finish(project
);
137 //dont return null for auto unboxing to not face NPE
138 return "boolean".equals(method
.getReturnType().getName()) ? Boolean
.TRUE
: null;
141 private void register(final Method method
, final Object
[] args
) {
142 final Object
[] newArr
= new Object
[args
.length
];
143 System
.arraycopy(args
, 0, newArr
, 0, args
.length
);
144 final Project project
= getProject(args
);
145 if (project
!= null) {
146 myMarker
.start(project
);
147 myStarted
.put(project
, new Pair
<String
, Object
[]>(method
.getName(), newArr
));
148 Disposer
.register(project
, new Disposable() {
149 public void dispose() {
150 myStarted
.remove(project
);
157 private static class MyInvoker
implements InvocationHandler
{
158 private final Object myDelegate
;
159 private final MyStorage myParent
;
160 private final LocalFileOperationsHandler myParentProxy
;
162 private MyInvoker(final MyStorage parent
, Object delegate
) {
164 myDelegate
= delegate
;
165 myParentProxy
= (LocalFileOperationsHandler
) Proxy
.newProxyInstance(LocalFileOperationsHandler
.class.getClassLoader(),
166 new Class
<?
>[]{LocalFileOperationsHandler
.class}, myParent
);
169 public Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{
170 if ("afterDone".equals(method
.getName()) && args
.length
== 1) {
171 ((ThrowableConsumer
<LocalFileOperationsHandler
, IOException
>)args
[0]).consume(myParentProxy
);
175 if (LocalFileOperationsHandler
.class.equals(method
.getDeclaringClass())) {
176 myParent
.register(method
, args
);
178 if ("equals".equals(method
.getName())) {
179 return args
[0].equals(this);
181 else if ("hashCode".equals(method
.getName())) {
184 return method
.invoke(myDelegate
, args
);