/*
* Copyright 2000-2016 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.util.indexing.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.SystemProperties;
import com.intellij.util.indexing.StorageException;
import gnu.trove.THashMap;
import gnu.trove.TObjectObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class MapInputDataDiffBuilder<Key, Value> extends InputDataDiffBuilder<Key, Value> {
private static final boolean ourDiffUpdateEnabled = SystemProperties.getBooleanProperty("idea.disable.diff.index.update", true);
private final Map<Key, Value> myMap;
public MapInputDataDiffBuilder(int inputId, @Nullable Map<Key, Value> map) {
super(inputId);
myMap = map == null ? Collections.<Key, Value>emptyMap() : map;
}
@Override
public void differentiate(@NotNull Map<Key, Value> newData,
@NotNull KeyValueUpdateProcessor<Key, Value> addProcessor,
@NotNull KeyValueUpdateProcessor<Key, Value> updateProcessor,
@NotNull RemovedKeyProcessor<Key> removeProcessor) throws StorageException {
if (ourDiffUpdateEnabled) {
if (myMap.isEmpty()) {
EmptyInputDataDiffBuilder.processKeys(newData, addProcessor, myInputId);
incrementalAdditions.addAndGet(newData.size());
}
else if (newData.isEmpty()) {
processAllKeysAsDeleted(removeProcessor);
incrementalRemovals.addAndGet(myMap.size());
}
else {
int added = 0;
int removed = 0;
for (Map.Entry<Key, Value> e: myMap.entrySet()) {
final Key key = e.getKey();
final Value newValue = newData.get(key);
if (!Comparing.equal(e.getValue(), newValue) || (newValue == null && !newData.containsKey(key))) {
if (!newData.containsKey(key)) {
removeProcessor.process(key, myInputId);
removed++;
} else {
updateProcessor.process(key, newValue, myInputId);
added++;
removed++;
}
}
}
for (Map.Entry<Key, Value> e : newData.entrySet()) {
final Key key = e.getKey();
if (!myMap.containsKey(key)) {
addProcessor.process(key, e.getValue(), myInputId);
added++;
}
}
incrementalAdditions.addAndGet(added);
incrementalRemovals.addAndGet(removed);
}
int totalRequests = requests.incrementAndGet();
totalRemovals.addAndGet(myMap.size());
totalAdditions.addAndGet(newData.size());
if ((totalRequests & 0xFFF) == 0 && DebugAssertions.DEBUG) {
Logger.getInstance(getClass()).info("Incremental index diff update:" + requests +
", removals:" + totalRemovals + "->" + incrementalRemovals +
", additions:" + totalAdditions + "->" + incrementalAdditions);
}
}
else {
CollectionInputDataDiffBuilder.differentiateWithKeySeq(myMap.keySet(), newData, myInputId, addProcessor, removeProcessor);
}
}
private void processAllKeysAsDeleted(final RemovedKeyProcessor<Key> removeProcessor) throws StorageException {
if (myMap instanceof THashMap) {
final StorageException[] exception = new StorageException[]{null};
((THashMap<Key, Value>)myMap).forEachEntry(new TObjectObjectProcedure<Key, Value>() {
@Override
public boolean execute(Key k, Value v) {
try {
removeProcessor.process(k, myInputId);
}
catch (StorageException e) {
exception[0] = e;
return false;
}
return true;
}
});
if (exception[0] != null) throw exception[0];
}
else {
for (Key key : myMap.keySet()) {
removeProcessor.process(key, myInputId);
}
}
}
private static final AtomicInteger requests = new AtomicInteger();
private static final AtomicInteger totalRemovals = new AtomicInteger();
private static final AtomicInteger totalAdditions = new AtomicInteger();
private static final AtomicInteger incrementalRemovals = new AtomicInteger();
private static final AtomicInteger incrementalAdditions = new AtomicInteger();
}