protected Indexes(
        ExecutionContext context, Collection<IndexDefinition> defns, NodeTypes nodeTypes) {
      // Identify the subtypes for each node type, and do this before we build any views ...
      if (!defns.isEmpty()) {
        Map<Name, Collection<String>> subtypesByName = new HashMap<>();
        for (JcrNodeType nodeType : nodeTypes.getAllNodeTypes()) {
          // For each of the supertypes ...
          for (JcrNodeType supertype : nodeType.getTypeAndSupertypes()) {
            Collection<String> types = subtypesByName.get(supertype.getInternalName());
            if (types == null) {
              types = new LinkedList<>();
              subtypesByName.put(supertype.getInternalName(), types);
            }
            types.add(nodeType.getName());
          }
        }

        // Now process all of the indexes ...
        NameFactory names = context.getValueFactories().getNameFactory();
        Set<Name> nodeTypeNames = new HashSet<>();
        for (IndexDefinition defn : defns) {

          // Determine all of the node types that are subtypes of any columns
          nodeTypeNames.clear();
          Name nodeTypeName = names.create(defn.getNodeTypeName());

          if (!subtypesByName.containsKey(nodeTypeName)) {
            Logger.getLogger(getClass())
                .warn(
                    JcrI18n.errorIndexing,
                    "not creating index "
                        + defn.getName()
                        + " because of unknown nodeType "
                        + nodeTypeName.getString());
            continue;
          }

          indexByName.put(defn.getName(), defn);

          // Now find out all of the node types that are or subtype the named node types ...
          for (String typeAndSubtype : subtypesByName.get(nodeTypeName)) {
            Map<String, Collection<IndexDefinition>> byProvider =
                indexesByProviderByNodeTypeName.get(typeAndSubtype);
            if (byProvider == null) {
              byProvider = new HashMap<>();
              indexesByProviderByNodeTypeName.put(typeAndSubtype, byProvider);
            }
            Collection<IndexDefinition> indexes = byProvider.get(defn.getProviderName());
            if (indexes == null) {
              indexes = new LinkedList<>();
              byProvider.put(typeAndSubtype, indexes);
            }
            indexes.add(defn);
          }
        }
      }
    }
  /**
   * Allows the collection of node types to be unregistered if they are not referenced by other node
   * types as supertypes, default primary types of child nodes, or required primary types of child
   * nodes.
   *
   * @param nodeTypeNames the names of the node types to be unregistered
   * @param failIfNodeTypesAreUsed true if this method should fail to unregister the named node
   *     types if any of the node types are still in use by nodes, or false if this method should
   *     not perform such a check
   * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered
   *     node type
   * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot
   *     be unregistered because they are the supertype, one of the required primary types, or a
   *     default primary type of a node type that is not being unregistered.
   * @throws RepositoryException if any other error occurs
   */
  void unregisterNodeType(Collection<Name> nodeTypeNames, boolean failIfNodeTypesAreUsed)
      throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException {
    CheckArg.isNotNull(nodeTypeNames, "nodeTypeNames");
    if (nodeTypeNames.isEmpty()) return;

    if (failIfNodeTypesAreUsed) {
      long start = System.nanoTime();
      // Search the content graph to make sure that this type isn't being used
      for (Name nodeTypeName : nodeTypeNames) {
        if (isNodeTypeInUse(nodeTypeName)) {
          String name = nodeTypeName.getString(context.getNamespaceRegistry());
          throw new InvalidNodeTypeDefinitionException(
              JcrI18n.cannotUnregisterInUseType.text(name));
        }
      }
      long time =
          TimeUnit.MILLISECONDS.convert(Math.abs(System.nanoTime() - start), TimeUnit.NANOSECONDS);
      logger.debug(
          "{0} milliseconds to check if any of these node types are unused before unregistering them: {1}",
          time, nodeTypeNames);
    }

    try {
      /*
       * Grab an exclusive lock on this data to keep other nodes from being added/saved while the unregistration checks are occurring
       */
      List<JcrNodeType> removedNodeTypes = new ArrayList<JcrNodeType>(nodeTypeNames.size());
      nodeTypesLock.writeLock().lock();
      final NodeTypes nodeTypes = this.nodeTypesCache;

      for (Name nodeTypeName : nodeTypeNames) {
        /*
         * Check that the type names are valid
         */
        if (nodeTypeName == null) {
          throw new NoSuchNodeTypeException(JcrI18n.invalidNodeTypeName.text());
        }
        String name = nodeTypeName.getString(context.getNamespaceRegistry());

        JcrNodeType foundNodeType = nodeTypes.getNodeType(nodeTypeName);
        if (foundNodeType == null) {
          throw new NoSuchNodeTypeException(JcrI18n.noSuchNodeType.text(name));
        }
        removedNodeTypes.add(foundNodeType);

        /*
         * Check that no other node definitions have dependencies on any of the named types
         */
        for (JcrNodeType nodeType : nodeTypes.getAllNodeTypes()) {
          // If this node is also being unregistered, don't run checks against it
          if (nodeTypeNames.contains(nodeType.getInternalName())) {
            continue;
          }

          for (JcrNodeType supertype : nodeType.supertypes()) {
            if (nodeTypeName.equals(supertype.getInternalName())) {
              throw new InvalidNodeTypeDefinitionException(
                  JcrI18n.cannotUnregisterSupertype.text(name, supertype.getName()));
            }
          }

          for (JcrNodeDefinition childNode : nodeType.childNodeDefinitions()) {
            NodeType defaultPrimaryType = childNode.getDefaultPrimaryType();
            if (defaultPrimaryType != null && name.equals(defaultPrimaryType.getName())) {
              throw new InvalidNodeTypeDefinitionException(
                  JcrI18n.cannotUnregisterDefaultPrimaryType.text(
                      name, nodeType.getName(), childNode.getName()));
            }
            if (childNode.requiredPrimaryTypeNameSet().contains(nodeTypeName)) {
              throw new InvalidNodeTypeDefinitionException(
                  JcrI18n.cannotUnregisterRequiredPrimaryType.text(
                      name, nodeType.getName(), childNode.getName()));
            }
          }
        }
      }

      // Create the new cache ...
      NodeTypes newNodeTypes = nodeTypes.without(removedNodeTypes);

      // Remove the node types from persistent storage ...
      SessionCache system = repository.createSystemSession(context, false);
      SystemContent systemContent = new SystemContent(system);
      systemContent.unregisterNodeTypes(
          removedNodeTypes.toArray(new JcrNodeType[removedNodeTypes.size()]));
      systemContent.save();

      // Now change the cache ...
      this.nodeTypesCache = newNodeTypes;
      this.schemata = null;
    } finally {
      nodeTypesLock.writeLock().unlock();
    }
  }