code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.sdk.server.api;
  
  import java.util.Collection;
  import java.util.List;
  import java.util.Optional;
  import java.util.concurrent.CopyOnWriteArrayList;
  import java.util.function.Predicate;
  import java.util.stream.Collectors;
  
  import org.eclipse.milo.opcua.sdk.core.Reference;
  import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
  import org.eclipse.milo.opcua.sdk.server.nodes.UaNode;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ExpandedNodeId;
  import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class AddressSpaceManager extends AddressSpaceComposite {
  
      private final Logger logger = LoggerFactory.getLogger(getClass());
  
      private final List<NodeManager<UaNode>> nodeManagers = new CopyOnWriteArrayList<>();
  
      public AddressSpaceManager(OpcUaServer server) {
          super(server);
      }
  
      /**
       * Register a {@link NodeManager} with this {@link AddressSpaceManager}.
       *
       * @param nodeManager the {@link NodeManager} to register.
       */
      public synchronized void register(NodeManager<UaNode> nodeManager) {
          if (!nodeManagers.contains(nodeManager)) {
              nodeManagers.add(nodeManager);
          } else {
              logger.warn("NodeManager already registered: {}", nodeManager);
          }
      }
  
      /**
       * Unregister a {@link NodeManager} with this {@link AddressSpaceManager}.
       *
       * @param nodeManager the {@link NodeManager} to register.
       */
      public synchronized void unregister(NodeManager<UaNode> nodeManager) {
          if (nodeManagers.contains(nodeManager)) {
              nodeManagers.remove(nodeManager);
          } else {
              logger.warn("NodeManager not registered: {}", nodeManager);
          }
      }
  
      /**
       * Get the managed {@link UaNode} identified by {@code nodeId} from the first registered {@link NodeManager} that
       * has it, if there is one.
       *
       * @param nodeId the {@link NodeId} identifying the managed {@link UaNode}.
       * @return the managed {@link UaNode} identified by {@code nodeId}, if there is one.
       */
      public Optional<UaNode> getManagedNode(NodeId nodeId) {
          return nodeManagers.stream()
              .filter(n -> n.containsNode(nodeId))
              .findFirst()
              .flatMap(n -> n.getNode(nodeId));
      }
  
      /**
       * Get the managed {@link UaNode} identified by {@code nodeId} from the first registered {@link NodeManager} that
       * has it, if there is one.
       *
       * @param nodeId the {@link ExpandedNodeId} identifying the managed {@link UaNode}.
       * @return the managed {@link UaNode} identified by {@code nodeId}, if there is one.
       */
      public Optional<UaNode> getManagedNode(ExpandedNodeId nodeId) {
          return nodeId.toNodeId(getServer().getNamespaceTable()).flatMap(this::getManagedNode);
      }
  
      /**
       * Collect all {@link Reference}s from all registered {@link NodeManager}s where {@code nodeId} is the source
       * {@link NodeId} in the Reference.
       *
       * @param nodeId the {@link NodeId} of the source NodeId in the {@link Reference}.
       * @return all {@link Reference}s from all registered {@link NodeManager}s where {@code nodeId} is the source
       * {@link NodeId} in the Reference.
       */
      public List<Reference> getManagedReferences(NodeId nodeId) {
          return nodeManagers.stream()
              .map(n -> n.getReferences(nodeId))
              .flatMap(Collection::stream)
              .collect(Collectors.toList());
      }
  
      /**
       * Collect all {@link Reference}s from all registered {@link NodeManager}s where {@code nodeId} is the source
       * {@link NodeId} in the Reference and the Reference passes {@code filter}.
       *
       * @param nodeId the {@link NodeId} of the source NodeId in the {@link Reference}.
       * @param filter a {@link Predicate} to apply to the collected {@link Reference}s.
       * @return all {@link Reference}s from all registered {@link NodeManager}s where {@code nodeId} is the source
       * {@link NodeId} in the Reference.
       */
      public List<Reference> getManagedReferences(NodeId nodeId, Predicate<Reference> filter) {
          return nodeManagers.stream()
              .map(n -> n.getReferences(nodeId, filter))
              .flatMap(Collection::stream)
              .collect(Collectors.toList());
      }
  
  }
  