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.nodes.filters;
  
  import java.util.Iterator;
  import java.util.Optional;
  
  import org.eclipse.milo.opcua.sdk.server.Session;
  import org.eclipse.milo.opcua.sdk.server.nodes.AttributeObserver;
  import org.eclipse.milo.opcua.sdk.server.nodes.UaNode;
  import org.eclipse.milo.opcua.stack.core.AttributeId;
  
  public abstract class AttributeFilterContext {
  
      private volatile boolean observable = false;
  
      private final UaNode node;
      private final Session session;
  
      private AttributeFilterContext(Session session, UaNode node) {
          this.session = session;
          this.node = node;
      }
  
      /**
       * Get the {@link UaNode} an attribute is being get from or set on.
       *
       * @return the {@link UaNode} an attribute is being get from or set on.
       */
      public UaNode getNode() {
          return node;
      }
  
      /**
       * Get the {@link Session} this the attribute get or set request is originating from, if there is one.
       * <p>
       * No session indicates the call is internal and should not fail for access / authentication reasons.
       *
       * @return the {@link Session} this the attribute get or set request is originating from, if there is one.
       */
      public Optional<Session> getSession() {
          return Optional.ofNullable(session);
      }
  
      /**
       * Indicate that an attribute get should be observed by {@link AttributeObserver}s.
       *
       * @param observable {@code true} if the attribute get should be observed by {@link AttributeObserver}s.
       */
      public void setObservable(boolean observable) {
          this.observable = observable;
      }
  
      /**
       * @return {@code true} if the attribute get should be observed by {@link AttributeObserver}s.
       */
      public boolean isObservable() {
          return observable;
      }
  
  
      public static final class GetAttributeContext extends AttributeFilterContext {
  
          private final Iterator<AttributeFilter> filterIterator;
  
          GetAttributeContext(Session session, UaNode node, Iterator<AttributeFilter> filterIterator) {
              super(session, node);
              this.filterIterator = filterIterator;
          }
  
          /**
           * Get the value for the attribute identified by {@code attributeId} using the next filter in the chain.
           *
           * @param attributeId the {@link AttributeId} of the attribute to get the value of.
           * @return the value for the attribute identified by {@code attributeId} from the next filter in the chain.
           */
          public Object getAttribute(AttributeId attributeId) {
              AttributeFilter next = filterIterator.hasNext() ?
                  filterIterator.next() :
                  AttributeFilter.DEFAULT_INSTANCE;
  
              return next.getAttribute(this, attributeId);
          }
  
      }
  
      public static final class SetAttributeContext extends AttributeFilterContext {
  
          private final Iterator<AttributeFilter> filterIterator;
  
          SetAttributeContext(Session session, UaNode node, Iterator<AttributeFilter> filterIterator) {
              super(session, node);
  
              this.filterIterator = filterIterator;
          }
  
          /**
           * Set the value for the attribute identified by {@code attributeId} using the next filter in the chain.
           *
           * @param attributeId the {@link AttributeId} of the attribute to set the value of.
           * @param value       the value to set.
           */
          public void setAttribute(AttributeId attributeId, Object value) {
              AttributeFilter next = filterIterator.hasNext() ?
                  filterIterator.next() :
                  AttributeFilter.DEFAULT_INSTANCE;
  
              next.setAttribute(this, attributeId, value);
          }
  
      }
  
  }
  