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.function.Predicate;
  
  import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
  import org.eclipse.milo.opcua.stack.core.NamespaceTable;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ExpandedNodeId;
  import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
  import org.eclipse.milo.opcua.stack.core.types.structured.AddNodesItem;
  import org.eclipse.milo.opcua.stack.core.types.structured.AddReferencesItem;
  import org.eclipse.milo.opcua.stack.core.types.structured.CallMethodRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.DeleteNodesItem;
  import org.eclipse.milo.opcua.stack.core.types.structured.DeleteReferencesItem;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryReadValueId;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryUpdateDetails;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
  import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
  
  /**
   * A simple {@link AddressSpaceFilter} that delegates each of the filter operations to a simple filter on {@link NodeId}
   * using the most reasonable NodeId from that operation's request as the filter criteria.
   */
  public abstract class SimpleAddressSpaceFilter implements AddressSpaceFilter {
  
      /**
       * Create a new {@link SimpleAddressSpaceFilter} that uses a {@link Predicate} on {@link NodeId}.
       *
       * @param nodeIdFilter a {@link Predicate} that tests a {@link NodeId}.
       * @return a new {@link SimpleAddressSpaceFilter} that uses a {@link Predicate} on {@link NodeId}.
       */
      public static SimpleAddressSpaceFilter create(Predicate<NodeId> nodeIdFilter) {
          return create(nodeIdFilter, nodeIdFilter);
      }
  
      /**
       * Create a new {@link SimpleAddressSpaceFilter} that uses a separate {@link Predicate} for {@link NodeId}s and
       * {@link MonitoredItem}s.
       *
       * @param nodeIdFilter        a {@link Predicate} that tests a {@link NodeId}.
       * @param monitoredItemFilter a {@link Predicate} that tests a {@link NodeId} targeted by a {@link MonitoredItem}.
       * @return a new {@link SimpleAddressSpaceFilter} that uses a {@link Predicate} on {@link NodeId}.
       */
      public static SimpleAddressSpaceFilter create(
          Predicate<NodeId> nodeIdFilter,
          Predicate<NodeId> monitoredItemFilter
      ) {
  
          return new SimpleAddressSpaceFilter() {
              @Override
              protected boolean filterNode(NodeId nodeId) {
                  return nodeIdFilter.test(nodeId);
              }
  
              @Override
              protected boolean filterMonitoredItem(NodeId nodeId) {
                  return monitoredItemFilter.test(nodeId);
              }
          };
      }
  
      /**
       * Return {@code true} if the operation {@code nodeId} belongs to should be handled this filter's
       * {@link AddressSpace}.
       * <p>
       * This is not an indication that a Node for {@code nodeId} exists, rather, it's an indication that this
       * AddressSpace would be responsible for the Node *if it did* exist.
       *
       * @param nodeId a {@link NodeId}.
       * @return {@code true} if the operation {@code nodeId} belongs to should be handled this filter's
       * {@link AddressSpace}.
       */
      protected abstract boolean filterNode(NodeId nodeId);
  
      /**
       * Return {@code true} if the operation on the MonitoredItem for {@code nodeId} should be
       * handled by this filter's {@link AddressSpace}.
       *
       * @param nodeId the {@link NodeId} of {@link MonitoredItem} being operated on.
       * @return {@code true} if the operation on the MonitoredItem for {@code nodeId} should be
       * handled by this filter's {@link AddressSpace}.
       */
      protected abstract boolean filterMonitoredItem(NodeId nodeId);
  
      //region ViewServices
  
      @Override
      public boolean filterBrowse(OpcUaServer server, NodeId nodeId) {
          return filterNode(nodeId);
      }
  
      @Override
      public boolean filterRegisterNode(OpcUaServer server, NodeId nodeId) {
          return filterNode(nodeId);
      }
  
      @Override
      public boolean filterUnregisterNode(OpcUaServer server, NodeId nodeId) {
          return filterNode(nodeId);
      }
  
      //endregion
  
      //region AttributeServices
  
      @Override
      public boolean filterRead(OpcUaServer server, ReadValueId readValueId) {
          return filterNode(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterWrite(OpcUaServer server, WriteValue writeValue) {
          return filterNode(writeValue.getNodeId());
      }
  
      @Override
      public boolean filterHistoryRead(OpcUaServer server, HistoryReadValueId historyReadValueId) {
          return filterNode(historyReadValueId.getNodeId());
      }
  
      @Override
      public boolean filterHistoryUpdate(OpcUaServer server, HistoryUpdateDetails historyUpdateDetails) {
          return filterNode(historyUpdateDetails.getNodeId());
      }
  
      //endregion
  
      //region MethodServices
  
      @Override
      public boolean filterCall(OpcUaServer server, CallMethodRequest callMethodRequest) {
          return filterNode(callMethodRequest.getObjectId());
      }
  
      //endregion
  
      //region MonitoredItemServices
  
      @Override
      public boolean filterOnCreateDataItem(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnModifyDataItem(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnCreateEventItem(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnModifyEventItem(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnDataItemsCreated(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnDataItemsModified(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnDataItemsDeleted(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnEventItemsCreated(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnEventItemsModified(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnEventItemsDeleted(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      @Override
      public boolean filterOnMonitoringModeChanged(OpcUaServer server, ReadValueId readValueId) {
          return filterMonitoredItem(readValueId.getNodeId());
      }
  
      //endregion
  
      //region NodeManagementServices
  
      @Override
      public boolean filterAddNodes(OpcUaServer server, AddNodesItem addNodesItem) {
          NamespaceTable namespaceTable = server.getNamespaceTable();
  
          ExpandedNodeId requestedNewNodeId =
              addNodesItem.getRequestedNewNodeId();
  
          if (requestedNewNodeId.isNotNull()) {
              return requestedNewNodeId
                  .toNodeId(namespaceTable)
                  .map(this::filterNode)
                  .orElse(false);
          } else {
              return addNodesItem.getParentNodeId()
                  .toNodeId(namespaceTable)
                  .map(this::filterNode)
                  .orElse(false);
          }
      }
  
      @Override
      public boolean filterDeleteNodes(OpcUaServer server, DeleteNodesItem deleteNodesItem) {
          return filterNode(deleteNodesItem.getNodeId());
      }
  
      @Override
      public boolean filterAddReferences(OpcUaServer server, AddReferencesItem addReferencesItem) {
          return filterNode(addReferencesItem.getSourceNodeId());
      }
  
      @Override
      public boolean filterDeleteReferences(OpcUaServer server, DeleteReferencesItem deleteReferencesItem) {
          return filterNode(deleteReferencesItem.getSourceNodeId());
      }
  
      //endregion
  
  }
  