blob: b60861caa698be83d3984859fedf36ee91ce04f3 [file] [log] [blame]
/*
* Copyright 2000-2010 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 com.intellij.openapi.vcs.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsEventsListenerManager;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
import com.intellij.openapi.vcs.update.UpdateEnvironment;
import com.intellij.util.Consumer;
import com.intellij.util.EventDispatcher;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
/**
* @author irengrig
* Date: 12/15/10
* Time: 5:50 PM
*/
public class VcsEventsListenerManagerImpl implements VcsEventsListenerManager, VcsEnvironmentsProxyCreator {
private final Wrapper<CheckinEnvironment> myCheckinWrapper;
private final Wrapper<UpdateEnvironment> myUpdateWrapper;
private final Wrapper<RollbackEnvironment> myRollbackWrapper;
public VcsEventsListenerManagerImpl() {
myCheckinWrapper = new Wrapper<CheckinEnvironment>(CheckinEnvironment.class);
myUpdateWrapper = new Wrapper<UpdateEnvironment>(UpdateEnvironment.class);
myRollbackWrapper = new Wrapper<RollbackEnvironment>(RollbackEnvironment.class);
}
@Override
public Object addCheckin(final Consumer<Pair<VcsKey, Consumer<CheckinEnvironment>>> consumer) {
return myCheckinWrapper.add(consumer);
}
@Override
public Object addUpdate(final Consumer<Pair<VcsKey, Consumer<UpdateEnvironment>>> consumer) {
return myUpdateWrapper.add(consumer);
}
@Override
public Object addRollback(final Consumer<Pair<VcsKey, Consumer<RollbackEnvironment>>> consumer) {
return myRollbackWrapper.add(consumer);
}
@Override
public void removeCheckin(Object key) {
myCheckinWrapper.remove(key);
}
@Override
public void removeUpdate(Object key) {
myUpdateWrapper.remove(key);
}
@Override
public void removeRollback(Object key) {
myRollbackWrapper.remove(key);
}
@Nullable
@Override
public CheckinEnvironment proxyCheckin(final VcsKey key, final CheckinEnvironment environment) {
return myCheckinWrapper.createProxy(key, environment);
}
@Nullable
@Override
public UpdateEnvironment proxyUpdate(final VcsKey key, final UpdateEnvironment environment) {
return myUpdateWrapper.createProxy(key, environment);
}
@Nullable
@Override
public RollbackEnvironment proxyRollback(final VcsKey key, final RollbackEnvironment environment) {
return myRollbackWrapper.createProxy(key, environment);
}
private static class Wrapper<T> {
private final Map<Object, EventListenerWrapperI> myListenersMap;
private final Map<VcsKey, EventDispatcher<EventListenerWrapperI>> myExistingMulticasters;
private final Class<T> myClazz;
private final Object myLock;
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.VcsEventsListenerManagerImpl.Wrapper");
private Wrapper(final Class<T> clazz) {
myClazz = clazz;
myListenersMap = new HashMap<Object, EventListenerWrapperI>();
myExistingMulticasters = Collections.synchronizedMap(new HashMap<VcsKey, EventDispatcher<EventListenerWrapperI>>());
myLock = new Object();
}
@Nullable
public T createProxy(final VcsKey key, @Nullable final T environment) {
if (environment == null) return null;
final EventDispatcher<EventListenerWrapperI> eventDispatcher;
synchronized (myLock) {
assert ! myExistingMulticasters.containsKey(key);
eventDispatcher = EventDispatcher.create(EventListenerWrapperI.class);
myExistingMulticasters.put(key, eventDispatcher);
for (EventListenerWrapperI wrapper : myListenersMap.values()) {
eventDispatcher.addListener(wrapper);
}
}
final T proxy = (T) Proxy.newProxyInstance(myClazz.getClassLoader(),
new Class[]{myClazz},
new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
method.setAccessible(true);
synchronized (myLock) {
eventDispatcher.getMulticaster().consume(
new Pair<VcsKey, Consumer<T>>(key, new Consumer<T>() {
@Override
public void consume(T t) {
try {
method.invoke(t, args);
}
catch (IllegalAccessException e) {
LOG.info(e);
}
catch (InvocationTargetException e) {
LOG.info(e);
}
}
}));
}
return method.invoke(environment, args);
}
});
return proxy;
}
public Object add(final Consumer<Pair<VcsKey,Consumer<T>>> consumer) {
final Object key = new Object();
synchronized (myLock) {
EventListenerWrapper<T> listenerWrapper = new EventListenerWrapper<T>(consumer);
myListenersMap.put(key, listenerWrapper);
for (EventDispatcher<EventListenerWrapperI> eventDispatcher : myExistingMulticasters.values()) {
eventDispatcher.addListener(listenerWrapper);
}
}
return key;
}
public void remove(Object key) {
synchronized (myLock) {
final EventListenerWrapperI listenerWrapper = myListenersMap.remove(key);
if (listenerWrapper != null) {
for (EventDispatcher<EventListenerWrapperI> dispatcher : myExistingMulticasters.values()) {
dispatcher.removeListener(listenerWrapper);
}
}
}
}
private interface EventListenerWrapperI<T> extends Consumer<Pair<VcsKey, Consumer<T>>>, EventListener {}
private static class EventListenerWrapper<T> implements EventListenerWrapperI<T> {
private final Consumer<Pair<VcsKey, Consumer<T>>> myConsumer;
public EventListenerWrapper(Consumer<Pair<VcsKey, Consumer<T>>> consumer) {
myConsumer = consumer;
}
@Override
public void consume(Pair<VcsKey, Consumer<T>> vcsKeyConsumerPair) {
myConsumer.consume(vcsKeyConsumerPair);
}
}
}
}