WalkStateGraphContext.java

  1. /**
  2.  *
  3.  * Copyright 2018-2020 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smack.c2s.internal;

  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.LinkedHashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;

  25. import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
  26. import org.jivesoftware.smack.fsm.LoginContext;
  27. import org.jivesoftware.smack.fsm.State;
  28. import org.jivesoftware.smack.fsm.StateDescriptor;
  29. import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
  30. import org.jivesoftware.smack.fsm.StateTransitionResult;
  31. import org.jivesoftware.smack.util.CollectionUtil;
  32. import org.jivesoftware.smack.util.Objects;

  33. import org.jxmpp.jid.parts.Resourcepart;

  34. public final class WalkStateGraphContext {
  35.     private final Class<? extends StateDescriptor> initialStateClass;
  36.     private final Class<? extends StateDescriptor> finalStateClass;
  37.     private final Class<? extends StateDescriptor> mandatoryIntermediateState;
  38.     private final LoginContext loginContext;

  39.     private final List<State> walkedStateGraphPath = new ArrayList<>();

  40.     /**
  41.      * A linked Map of failed States with their reason as value.
  42.      */
  43.     final Map<State, StateTransitionResult> failedStates = new LinkedHashMap<>();

  44.     boolean mandatoryIntermediateStateHandled;

  45.     WalkStateGraphContext(Builder builder) {
  46.         initialStateClass = builder.initialStateClass;
  47.         finalStateClass = builder.finalStateClass;
  48.         mandatoryIntermediateState = builder.mandatoryIntermediateState;
  49.         loginContext = builder.loginContext;
  50.     }

  51.     public void recordWalkTo(State state) {
  52.         walkedStateGraphPath.add(state);
  53.     }

  54.     public boolean isWalksFinalState(StateDescriptor stateDescriptor) {
  55.         return stateDescriptor.getClass() == finalStateClass;
  56.     }

  57.     public boolean isFinalStateAuthenticatedAndResourceBound() {
  58.         return finalStateClass == AuthenticatedAndResourceBoundStateDescriptor.class;
  59.     }

  60.     public GraphVertex<State> maybeReturnMandatoryImmediateState(List<GraphVertex<State>> outgoingStateEdges) {
  61.         for (GraphVertex<State> outgoingStateVertex : outgoingStateEdges) {
  62.             if (outgoingStateVertex.getElement().getStateDescriptor().getClass() == mandatoryIntermediateState) {
  63.                 mandatoryIntermediateStateHandled = true;
  64.                 return outgoingStateVertex;
  65.             }
  66.         }

  67.         return null;
  68.     }

  69.     public List<State> getWalk() {
  70.         return CollectionUtil.newListWith(walkedStateGraphPath);
  71.     }

  72.     public int getWalkLength() {
  73.         return walkedStateGraphPath.size();
  74.     }

  75.     public void appendWalkTo(List<State> walk) {
  76.         walk.addAll(walkedStateGraphPath);
  77.     }

  78.     public LoginContext getLoginContext() {
  79.         return loginContext;
  80.     }

  81.     public boolean stateAlreadyVisited(State state) {
  82.         return walkedStateGraphPath.contains(state);
  83.     }

  84.     public void recordFailedState(State state, StateTransitionResult stateTransitionResult) {
  85.         failedStates.put(state, stateTransitionResult);
  86.     }

  87.     public Map<State, StateTransitionResult> getFailedStates() {
  88.         return new HashMap<>(failedStates);
  89.     }

  90.     /**
  91.      * Check if the way to the final state via the given successor state that would loop, i.e., lead over the initial state and
  92.      * thus from a cycle.
  93.      *
  94.      * @param successorStateVertex the successor state to use on the way.
  95.      * @return <code>true</code> if it would loop, <code>false</code> otherwise.
  96.      */
  97.     public boolean wouldCauseCycle(GraphVertex<State> successorStateVertex) {
  98.         Set<Class<? extends StateDescriptor>> visited = new HashSet<>();
  99.         return wouldCycleRecursive(successorStateVertex, visited);
  100.     }

  101.     private boolean wouldCycleRecursive(GraphVertex<State> stateVertex, Set<Class<? extends StateDescriptor>> visited) {
  102.         Class<? extends StateDescriptor> stateVertexClass = stateVertex.getElement().getStateDescriptor().getClass();

  103.         if (stateVertexClass == initialStateClass) {
  104.             return true;
  105.         }
  106.         if (finalStateClass == stateVertexClass || visited.contains(stateVertexClass)) {
  107.             return false;
  108.         }

  109.         visited.add(stateVertexClass);

  110.         for (GraphVertex<State> successorStateVertex : stateVertex.getOutgoingEdges()) {
  111.             boolean cycle = wouldCycleRecursive(successorStateVertex, visited);
  112.             if (cycle) {
  113.                 return true;
  114.             }
  115.         }

  116.         return false;
  117.     }

  118.     public static Builder builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
  119.         return new Builder(initialStateClass, finalStateClass);
  120.     }

  121.     public static final class Builder {
  122.         private final Class<? extends StateDescriptor> initialStateClass;
  123.         private final Class<? extends StateDescriptor> finalStateClass;
  124.         private Class<? extends StateDescriptor> mandatoryIntermediateState;
  125.         private LoginContext loginContext;

  126.         private Builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
  127.             this.initialStateClass = Objects.requireNonNull(initialStateClass);
  128.             this.finalStateClass = Objects.requireNonNull(finalStateClass);
  129.         }

  130.         public Builder withMandatoryIntermediateState(Class<? extends StateDescriptor> mandatoryIntermedidateState) {
  131.             this.mandatoryIntermediateState = mandatoryIntermedidateState;
  132.             return this;
  133.         }

  134.         public Builder withLoginContext(String username, String password, Resourcepart resource) {
  135.             LoginContext loginContext = new LoginContext(username, password, resource);
  136.             return withLoginContext(loginContext);
  137.         }

  138.         public Builder withLoginContext(LoginContext loginContext) {
  139.             this.loginContext = loginContext;
  140.             return this;
  141.         }

  142.         public WalkStateGraphContext build() {
  143.             return new WalkStateGraphContext(this);
  144.         }
  145.     }
  146. }