code: /*
   * Copyright (c) 2021 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.client;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.concurrent.CompletableFuture;
  
  import org.eclipse.milo.opcua.stack.client.UaStackClient;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
  import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
  import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
  import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseDescription;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseNextRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseNextResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseResult;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReferenceDescription;
  import org.eclipse.milo.opcua.stack.core.types.structured.ViewDescription;
  
  import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
  import static org.eclipse.milo.opcua.stack.core.util.ConversionUtil.l;
  
  /**
   * "Helper" functions for doing a Browse followed by as many BrowseNext calls as are necessary
   * to retrieve all the references.
   */
  public class BrowseHelper {
  
      private BrowseHelper() {}
  
      public static CompletableFuture<List<ReferenceDescription>> browse(
          OpcUaClient client,
          BrowseDescription browseDescription,
          UInteger maxReferencesPerNode
      ) {
  
          return client.getSession().thenCompose(
              session ->
                  browse(client.getStackClient(), session, browseDescription, maxReferencesPerNode)
          );
      }
  
      public static CompletableFuture<List<ReferenceDescription>> browse(
          UaStackClient client,
          OpcUaSession session,
          BrowseDescription browseDescription,
          UInteger maxReferencesPerNode
      ) {
  
          BrowseRequest browseRequest = new BrowseRequest(
              client.newRequestHeader(
                  session.getAuthenticationToken(),
                  client.getConfig().getRequestTimeout()
              ),
              new ViewDescription(
                  NodeId.NULL_VALUE,
                  DateTime.MIN_VALUE,
                  uint(0)
              ),
              maxReferencesPerNode,
              new BrowseDescription[]{browseDescription}
          );
  
          return client.sendRequest(browseRequest).thenApply(BrowseResponse.class::cast).thenCompose(response -> {
              BrowseResult result = response.getResults()[0];
  
              List<ReferenceDescription> references =
                  Collections.synchronizedList(new ArrayList<>());
  
              return maybeBrowseNext(client, session, references, result);
          });
      }
  
      private static CompletableFuture<List<ReferenceDescription>> maybeBrowseNext(
          UaStackClient client,
          OpcUaSession session,
          List<ReferenceDescription> references,
          BrowseResult result
      ) {
  
          if (result.getStatusCode().isGood()) {
              references.addAll(l(result.getReferences()));
  
              ByteString nextContinuationPoint = result.getContinuationPoint();
  
              if (nextContinuationPoint == null || nextContinuationPoint.isNull()) {
                  return CompletableFuture.completedFuture(references);
              } else {
                  return browseNext(client, session, nextContinuationPoint, references);
              }
          } else {
              return CompletableFuture.completedFuture(references);
          }
      }
  
      private static CompletableFuture<List<ReferenceDescription>> browseNext(
          UaStackClient client,
          OpcUaSession session,
          ByteString continuationPoint,
          List<ReferenceDescription> references
      ) {
  
          BrowseNextRequest browseNextRequest = new BrowseNextRequest(
              client.newRequestHeader(
                  session.getAuthenticationToken(),
                  client.getConfig().getRequestTimeout()
              ),
              false,
              new ByteString[]{continuationPoint}
          );
  
          return client.sendRequest(browseNextRequest).thenApply(BrowseNextResponse.class::cast).thenCompose(response -> {
              BrowseResult result = response.getResults()[0];
  
              return maybeBrowseNext(client, session, references, result);
          });
      }
  
  }
  