public void integrate(final Mappings delta) {
    synchronized (myLock) {
      long start = System.currentTimeMillis();
      try {
        assert (delta.isDifferentiated());

        final Collection<String> removed = delta.myRemovedFiles;

        delta.runPostPasses();

        final IntIntMultiMaplet dependenciesTrashBin = new IntIntTransientMultiMaplet();

        if (removed != null) {
          for (final String file : removed) {
            final File deletedFile = new File(file);
            final Set<ClassFileRepr> fileClasses = (Set<ClassFileRepr>)sourceFileToClassesGet(deletedFile);

            if (fileClasses != null) {
              for (final ClassFileRepr aClass : fileClasses) {
                cleanupRemovedClass(delta, aClass, deletedFile, aClass.getUsages(), dependenciesTrashBin);
              }
              myRelativeSourceFilePathToClasses.remove(myRelativizer.toRelative(file));
            }
          }
        }

        if (!delta.isRebuild()) {
          for (final Pair<ClassFileRepr, File> pair : delta.getDeletedClasses()) {
            final ClassFileRepr deletedClass = pair.first;
            cleanupRemovedClass(delta, deletedClass, pair.second, deletedClass.getUsages(), dependenciesTrashBin);
          }
          for (ClassRepr repr : delta.getAddedClasses()) {
            if (!repr.isAnonymous() && !repr.isLocal()) {
              myShortClassNameIndex.put(myContext.get(repr.getShortName()), repr.name);
            }
          }

          final IntSet superClasses = new IntOpenHashSet();
          final IntIntTransientMultiMaplet addedSuperClasses = delta.getAddedSuperClasses();
          final IntIntTransientMultiMaplet removedSuperClasses = delta.getRemovedSuperClasses();

          addAllKeys(superClasses, addedSuperClasses);
          addAllKeys(superClasses, removedSuperClasses);

          superClasses.forEach(superClass -> {
            final IntSet added = addedSuperClasses.get(superClass);
            IntSet removed12 = removedSuperClasses.get(superClass);

            final IntSet old = myClassToSubclasses.get(superClass);

            if (old == null) {
              if (added != null && !added.isEmpty()) {
                myClassToSubclasses.replace(superClass, added);
              }
            }
            else {
              boolean changed = false;
              final int[] addedAsArray = added == null || added.isEmpty() ? null : added.toIntArray();
              if (removed12 != null && !removed12.isEmpty()) {
                if (addedAsArray != null) {
                  // optimization: avoid unnecessary changes in the set
                  removed12 = new IntOpenHashSet(removed12);
                  removed12.removeAll(IntSet.of(addedAsArray));
                }
                if (!removed12.isEmpty()) {
                  changed = old.removeAll(removed12);
                }
              }

              if (addedAsArray != null) {
                changed |= old.addAll(IntSet.of(addedAsArray));
              }

              if (changed) {
                myClassToSubclasses.replace(superClass, old);
              }
            }
          });

          final Set<String> changedRelativePaths = CollectionFactory.createFilePathSet();
          for (File file : delta.getChangedFiles()) {
            changedRelativePaths.add(toRelative(file));
          }

          delta.getChangedClasses().forEach(new IntConsumer() {
            final Set<String> pathsBuffer = CollectionFactory.createFilePathSet();
            @Override
            public void accept(int className) {
              Collection<String> currentPaths = myClassToRelativeSourceFilePath.get(className);
              if (currentPaths != null && !currentPaths.isEmpty()) {
                try {
                  pathsBuffer.addAll(currentPaths);
                  pathsBuffer.removeAll(changedRelativePaths);
                  pathsBuffer.addAll(delta.myClassToRelativeSourceFilePath.get(className));
                  if (pathsBuffer.size() != currentPaths.size() || !pathsBuffer.containsAll(currentPaths)) {
                    myClassToRelativeSourceFilePath.replace(className, pathsBuffer);
                  }
                }
                finally {
                  pathsBuffer.clear();
                }
              }
              else {
                myClassToRelativeSourceFilePath.replace(className, delta.myClassToRelativeSourceFilePath.get(className));
              }

              cleanupBackDependency(className, null, dependenciesTrashBin);
            }
          });

          for (String path : changedRelativePaths) {
            myRelativeSourceFilePathToClasses.replace(path, delta.myRelativeSourceFilePathToClasses.get(path));
          }

          // some classes may be associated with multiple sources.
          // In case some of these sources was not compiled, but the class was changed, we need to update
          // sourceToClasses mapping for such sources to include the updated ClassRepr version of the changed class
          Set<File> unchangedSources = FileCollectionFactory.createCanonicalFileSet();
          delta.myRelativeSourceFilePathToClasses.forEachEntry((source, b) -> {
            unchangedSources.add(toFull(source));
            return true;
          });
          unchangedSources.removeAll(delta.getChangedFiles());
          if (!unchangedSources.isEmpty()) {
            unchangedSources.forEach(unchangedSource -> {
              final Collection<ClassFileRepr> updatedClasses = delta.sourceFileToClassesGet(unchangedSource);
              if (updatedClasses != null && !updatedClasses.isEmpty()) {
                final List<ClassFileRepr> classesToPut = new ArrayList<>();
                final IntSet updatedClassNames = new IntOpenHashSet();
                for (ClassFileRepr aClass : updatedClasses) {
                  // from all generated classes on this round consider only 'differentiated' ones, for
                  // which we can reliably say that the class has changed. Keep classes, for which no such checks were made,
                  // to make it possible to create a diff and compare changes on next compilation rounds.
                  if (delta.getChangedClasses().contains(aClass.name)) {
                    classesToPut.add(aClass);
                    updatedClassNames.add(aClass.name);
                  }
                }
                final Collection<ClassFileRepr> currentClasses = sourceFileToClassesGet(unchangedSource);
                if (currentClasses != null) {
                  for (ClassFileRepr aClass : currentClasses) {
                    if (!updatedClassNames.contains(aClass.name)) {
                      classesToPut.add(aClass);
                    }
                  }
                }
                myRelativeSourceFilePathToClasses.replace(toRelative(unchangedSource), classesToPut);
              }
            });
          }
        }
        else {
          myClassToSubclasses.putAll(delta.myClassToSubclasses);
          myClassToRelativeSourceFilePath.putAll(delta.myClassToRelativeSourceFilePath);
          myRelativeSourceFilePathToClasses.replaceAll(delta.myRelativeSourceFilePathToClasses);
          delta.myRelativeSourceFilePathToClasses.forEachEntry(
            (src, classes) -> {
              for (ClassFileRepr repr : classes) {
                if (repr instanceof ClassRepr) {
                  final ClassRepr clsRepr = (ClassRepr)repr;
                  if (!clsRepr.isAnonymous() && !clsRepr.isLocal()) {
                    myShortClassNameIndex.put(myContext.get(clsRepr.getShortName()), repr.name);
                  }
                }
              }
              return true;
            });
        }

        // updating classToClass dependencies

        final IntSet affectedClasses = new IntOpenHashSet();

        addAllKeys(affectedClasses, dependenciesTrashBin);
        addAllKeys(affectedClasses, delta.myClassToClassDependency);

        affectedClasses.forEach(aClass -> {
          final IntSet toRemove = dependenciesTrashBin.get(aClass);
          if (toRemove != null) {
            myClassToClassDependency.removeAll(aClass, toRemove);
          }
          final IntSet toAdd = delta.myClassToClassDependency.get(aClass);
          if (toAdd != null) {
            myClassToClassDependency.put(aClass, toAdd);
          }
        });
      }
      finally {
        delta.close();
        myTotalIntegrateTime += (System.currentTimeMillis() - start);
      }
    }
  }
public void integrate(final Mappings delta)