public Callbacks.Backend getCallback() {
    return new Callbacks.Backend() {
      // className -> {imports; static_imports}
      private final Map<String, Pair<Collection<String>, Collection<String>>> myImportRefs = Collections.synchronizedMap(new HashMap<>());
      private final Map<String, Collection<Callbacks.ConstantRef>> myConstantRefs = Collections.synchronizedMap(new HashMap<>());

      @Override
      public void associate(String classFileName, Collection<String> sources, ClassReader cr, boolean isGenerated) {
        synchronized (myLock) {
          final int classFileNameS = myContext.get(classFileName);
          final ClassFileRepr result = new ClassfileAnalyzer(myContext).analyze(classFileNameS, cr, isGenerated);
          if (result != null) {
            // since java9 'repr' can represent either a class or a compiled module-info.java
            final int className = result.name;

            if (result instanceof ClassRepr) {
              final ClassRepr classRepr = (ClassRepr)result;
              final String classNameStr = myContext.getValue(className);
              if (addConstantUsages(classRepr, myConstantRefs.remove(classNameStr))) {
                // Important: should register constants before imports, because imports can produce additional
                // field references too and addConstantUsages may return false in this case
                classRepr.setHasInlinedConstants(true);
              }
              final Pair<Collection<String>, Collection<String>> imports = myImportRefs.remove(classNameStr);
              if (imports != null) {
                addImportUsages(classRepr, imports.getFirst(), imports.getSecond());
              }
            }

            for (String sourceFileName : sources) {
              String relative = myRelativizer.toRelative(sourceFileName);
              myClassToRelativeSourceFilePath.put(className, relative);
              myRelativeSourceFilePathToClasses.put(relative, result);
            }

            if (result instanceof ClassRepr) {
              for (final TypeRepr.ClassType s : ((ClassRepr)result).getSuperTypes()) {
                myClassToSubclasses.put(s.className, className);
              }
            }

            for (final UsageRepr.Usage u : result.getUsages()) {
              final int owner = u.getOwner();
              if (owner != className) {
                myClassToClassDependency.put(owner, className);
              }
            }
          }
        }
      }

      @Override
      public void registerImports(String className, Collection<String> classImports, Collection<String> staticImports) {
        final String key = className.replace('.', '/');
        if (!classImports.isEmpty() || !staticImports.isEmpty()) {
          myImportRefs.put(key, Pair.create(classImports, staticImports));
        }
        else {
          myImportRefs.remove(key);
        }
      }

      @Override
      public void registerConstantReferences(String className, Collection<Callbacks.ConstantRef> cRefs) {
        final String key = className.replace('.', '/');
        if (!cRefs.isEmpty()) {
          myConstantRefs.put(key, cRefs);
        }
        else {
          myConstantRefs.remove(key);
        }
      }

      private void addImportUsages(ClassRepr repr, Collection<String> classImports, Collection<String> staticImports) {
        for (final String anImport : classImports) {
          if (!anImport.endsWith(IMPORT_WILDCARD_SUFFIX)) {
            repr.addUsage(UsageRepr.createClassUsage(myContext, myContext.get(anImport.replace('.', '/'))));
          }
        }
        for (String anImport : staticImports) {
          if (anImport.endsWith(IMPORT_WILDCARD_SUFFIX)) {
            final int iname = myContext.get(anImport.substring(0, anImport.length() - IMPORT_WILDCARD_SUFFIX.length()).replace('.', '/'));
            repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
            repr.addUsage(UsageRepr.createImportStaticOnDemandUsage(myContext, iname));
          }
          else {
            final int i = anImport.lastIndexOf('.');
            if (i > 0 && i < anImport.length() - 1) {
              final int iname = myContext.get(anImport.substring(0, i).replace('.', '/'));
              final int memberName = myContext.get(anImport.substring(i+1));
              repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
              repr.addUsage(UsageRepr.createImportStaticMemberUsage(myContext, memberName, iname));
            }
          }
        }
      }

      private boolean addConstantUsages(ClassRepr repr, Collection<? extends Callbacks.ConstantRef> cRefs) {
        boolean addedNewUsages = false;
        if (cRefs != null) {
          for (Callbacks.ConstantRef ref : cRefs) {
            final int owner = myContext.get(ref.getOwner().replace('.', '/'));
            if (repr.name != owner) {
              addedNewUsages |= repr.addUsage(UsageRepr.createFieldUsage(myContext, myContext.get(ref.getName()), owner, myContext.get(ref.getDescriptor())));
            }
          }
        }
        return addedNewUsages;
      }
    };
  }
public Callbacks.Backend getCallback()