/**
  * Register a set of namespaces.
  *
  * @param namespaceUrisByPrefix the map of new namespace URIs by their prefix
  */
 public void register(Map<String, String> namespaceUrisByPrefix) {
   if (namespaceUrisByPrefix == null || namespaceUrisByPrefix.isEmpty()) return;
   final Lock lock = this.namespacesLock.writeLock();
   try {
     lock.lock();
     SystemContent systemContent = systemContent(false);
     systemContent.registerNamespaces(namespaceUrisByPrefix);
     systemContent.save();
     for (Map.Entry<String, String> entry : namespaceUrisByPrefix.entrySet()) {
       String prefix = entry.getKey().trim();
       String uri = entry.getValue().trim();
       if (prefix.length() == 0) continue;
       this.cache.register(prefix, uri);
     }
   } finally {
     lock.unlock();
   }
 }
 @Override
 public boolean unregister(String namespaceUri) {
   CheckArg.isNotNull(namespaceUri, "namespaceUri");
   namespaceUri = namespaceUri.trim();
   final Lock lock = this.namespacesLock.writeLock();
   try {
     lock.lock();
     // Remove it from the cache ...
     boolean found = this.cache.unregister(namespaceUri);
     // Then from the source ...
     SystemContent systemContent = systemContent(false);
     boolean foundPersistent = systemContent.unregisterNamespace(namespaceUri);
     systemContent.save();
     return foundPersistent || found;
   } finally {
     lock.unlock();
   }
 }
 @Override
 public String register(String prefix, String namespaceUri) {
   CheckArg.isNotNull(namespaceUri, "namespaceUri");
   namespaceUri = namespaceUri.trim();
   final Lock lock = this.namespacesLock.writeLock();
   try {
     lock.lock();
     // Register it in the cache first ...
     String previousCachedUriForPrefix = this.cache.register(prefix, namespaceUri);
     if (!namespaceUri.equals(previousCachedUriForPrefix)) {
       // And register it in the source ...
       SystemContent systemContent = systemContent(false);
       systemContent.registerNamespaces(Collections.singletonMap(prefix, namespaceUri));
       systemContent.save();
     }
     return previousCachedUriForPrefix;
   } finally {
     lock.unlock();
   }
 }
 @Override
 public String getPrefixForNamespaceUri(String namespaceUri, boolean generateIfMissing) {
   CheckArg.isNotNull(namespaceUri, "namespaceUri");
   Lock lock = this.namespacesLock.readLock();
   try {
     lock.lock();
     // Try the cache first ...
     String prefix = cache.getPrefixForNamespaceUri(namespaceUri, false);
     if (prefix == null && generateIfMissing) {
       SystemContent systemContent = systemContent(!generateIfMissing);
       prefix = systemContent.readNamespacePrefix(namespaceUri, generateIfMissing);
       if (prefix != null) {
         systemContent.save();
         cache.register(prefix, namespaceUri);
       }
     }
     return prefix;
   } finally {
     lock.unlock();
   }
 }
  @Override
  public void unregisterIndexes(String... indexNames)
      throws NoSuchIndexException, RepositoryException {
    if (indexNames == null || indexNames.length == 0) return;

    // Remove the definition from the system area ...
    SessionCache systemCache = repository.createSystemSession(context, false);
    SystemContent system = new SystemContent(systemCache);
    for (String indexName : indexNames) {
      IndexDefinition defn = indexes.getIndexDefinitions().get(indexName);
      if (defn == null) {
        throw new NoSuchIndexException(
            JcrI18n.indexDoesNotExist.text(indexName, repository.name()));
      }
      system.remove(defn);
    }
    system.save();

    // Refresh the immutable snapshot ...
    this.indexes = readIndexDefinitions();
  }
  List<JcrNodeType> registerNodeTypes(
      Iterable<NodeTypeDefinition> nodeTypeDefns,
      boolean failIfNodeTypeDefinitionsExist,
      boolean skipIfNodeTypeDefinitionExists,
      boolean persist)
      throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, RepositoryException {

    if (nodeTypeDefns == null) {
      return Collections.emptyList();
    }

    List<JcrNodeType> typesPendingRegistration = new ArrayList<JcrNodeType>();

    try {
      nodeTypesLock.writeLock().lock();
      final NodeTypes nodeTypes = this.nodeTypesCache;

      for (NodeTypeDefinition nodeTypeDefn : nodeTypeDefns) {
        if (nodeTypeDefn instanceof JcrNodeTypeTemplate) {
          // Switch to use this context, so names are properly prefixed ...
          nodeTypeDefn = ((JcrNodeTypeTemplate) nodeTypeDefn).with(context);
        }
        Name internalName = nodeTypes.nameFactory().create(nodeTypeDefn.getName());
        if (internalName == null || internalName.getLocalName().length() == 0) {
          throw new InvalidNodeTypeDefinitionException(JcrI18n.invalidNodeTypeName.text());
        }

        boolean found = nodeTypes.hasNodeType(internalName);
        if (found && failIfNodeTypeDefinitionsExist) {
          String name = nodeTypeDefn.getName();
          throw new NodeTypeExistsException(internalName, JcrI18n.nodeTypeAlreadyExists.text(name));
        }
        if (found && skipIfNodeTypeDefinitionExists) continue;

        List<JcrNodeType> supertypes =
            nodeTypes.supertypesFor(nodeTypeDefn, typesPendingRegistration);
        JcrNodeType nodeType = nodeTypeFrom(nodeTypeDefn, supertypes);

        typesPendingRegistration.add(nodeType);
      }

      if (!typesPendingRegistration.isEmpty()) {
        // Make sure the nodes have primary types that are either already registered, or pending
        // registration ...
        validateTypes(typesPendingRegistration);

        // Validate each of types that should be registered
        for (JcrNodeType typePendingRegistration : typesPendingRegistration) {
          nodeTypes.validate(
              typePendingRegistration,
              Arrays.asList(typePendingRegistration.getDeclaredSupertypes()),
              typesPendingRegistration);
        }

        SystemContent system = null;
        if (persist) {
          SessionCache systemCache = repository.createSystemSession(context, false);
          system = new SystemContent(systemCache);
        }

        for (JcrNodeType nodeType : typesPendingRegistration) {
          if (system != null) system.store(nodeType, true);
        }

        // Create the new cache ...
        NodeTypes newNodeTypes = nodeTypes.with(typesPendingRegistration);

        // Save the changes ...
        if (system != null) system.save();

        // And finally update the capabilities cache ...
        this.nodeTypesCache = newNodeTypes;
        this.schemata = null;
      }
    } finally {
      nodeTypesLock.writeLock().unlock();
    }

    return typesPendingRegistration;
  }
  /**
   * 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();
    }
  }