/* * Copyright 2003-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 jetbrains.mps.classloading; import jetbrains.mps.module.ReloadableModuleBase; import jetbrains.mps.module.ReloadableModuleBase.SModuleDependenciesListener; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.openapi.module.SRepository; import org.jetbrains.mps.openapi.module.SRepositoryContentAdapter; import org.jetbrains.mps.openapi.module.SRepositoryListener; import org.jetbrains.mps.openapi.module.event.SModuleAddedEvent; import org.jetbrains.mps.openapi.module.event.SModuleChangedEvent; import org.jetbrains.mps.openapi.module.event.SModuleRemovedEvent; import org.jetbrains.mps.openapi.module.event.SRepositoryEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This class transforms non-batched events to batch events * It listens to usual SRepositoryListener events and groups them into SRepositoryEvents * not thread-safe */ public class BatchEventsProcessor { private static Logger LOG = LogManager.getLogger(BatchEventsProcessor.class); private volatile boolean myBatchStarted = false; private static final Object LOCK = new Object(); private final List<SRepositoryEvent> myEvents = new ArrayList<SRepositoryEvent>(); private final SRepositoryListener myRepositoryListener = new MySRepositoryListener(); private final SRepository myRepository; public BatchEventsProcessor(SRepository repository) { myRepository = repository; } public void startBatching() { if (myBatchStarted) { myBatchStarted = false; myEvents.clear(); throw new IllegalStateException("Batching has been already started; Clearing the queue..."); } if (!myEvents.isEmpty()) { LOG.warn("Events have not been flushed"); } myBatchStarted = true; } /** * flushes all accumulated events * stops listening to SRepository, if no new events are discovered * @return result of batching: a list of SRepositoryEvents */ public List<SRepositoryEvent> flush() { synchronized (LOCK) { if (!myBatchStarted) { return Collections.emptyList(); } List<SRepositoryEvent> result = new ArrayList<>(myEvents); myEvents.clear(); return result; } } public void finishBatching() { if (!myBatchStarted) throw new IllegalStateException("Batching has not been even started"); myBatchStarted = false; } public void dispose() { myRepository.removeRepositoryListener(myRepositoryListener); } public void init() { myRepository.addRepositoryListener(myRepositoryListener); } /** * This class listens for module's add/removal, for 'moduleChanged' event (triggered by AbstractModule) * and for internal (so far) 'dependenciesChanged' event. */ private class MySRepositoryListener extends SRepositoryContentAdapter implements SModuleDependenciesListener { private void addEventToList(@NotNull SRepositoryEvent event) { synchronized (LOCK) { myEvents.add(event); } } @Override public void moduleAdded(@NotNull SModule module) { if (!myBatchStarted) return; if (module instanceof ReloadableModuleBase) { module.addModuleListener(this); ((ReloadableModuleBase) module).addDependenciesListener(this); } addEventToList(new SModuleAddedEvent(module)); } @Override public void beforeModuleRemoved(@NotNull SModule module) { if (!myBatchStarted) return; if (module instanceof ReloadableModuleBase) { ((ReloadableModuleBase) module).removeDependenciesListener(this); module.removeModuleListener(this); } } @Override public void moduleRemoved(@NotNull SModuleReference mRef) { if (!myBatchStarted) return; addEventToList(new SModuleRemovedEvent(mRef, myRepository)); } @Override public void moduleChanged(@NotNull SModule module) { if (!myBatchStarted) return; addEventToList(new SModuleChangedEvent(module)); } @Override public void dependenciesChanged(@NotNull ReloadableModuleBase module) { if (!myBatchStarted) return; moduleChanged(module); } } }