mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-06-28 14:24:49 +02:00
180 lines
6.6 KiB
Java
180 lines
6.6 KiB
Java
|
/**
|
||
|
*
|
||
|
* Copyright 2018-2020 Florian Schmaus
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
package org.jivesoftware.smack.c2s.internal;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.LinkedHashMap;
|
||
|
import java.util.List;
|
||
|
import java.util.Map;
|
||
|
import java.util.Set;
|
||
|
|
||
|
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
|
||
|
import org.jivesoftware.smack.fsm.LoginContext;
|
||
|
import org.jivesoftware.smack.fsm.State;
|
||
|
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||
|
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||
|
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||
|
import org.jivesoftware.smack.util.Objects;
|
||
|
|
||
|
import org.jxmpp.jid.parts.Resourcepart;
|
||
|
|
||
|
public final class WalkStateGraphContext {
|
||
|
private final Class<? extends StateDescriptor> initialStateClass;
|
||
|
private final Class<? extends StateDescriptor> finalStateClass;
|
||
|
private final Class<? extends StateDescriptor> mandatoryIntermediateState;
|
||
|
private final LoginContext loginContext;
|
||
|
|
||
|
private final List<State> walkedStateGraphPath = new ArrayList<>();
|
||
|
|
||
|
/**
|
||
|
* A linked Map of failed States with their reason as value.
|
||
|
*/
|
||
|
final Map<State, StateTransitionResult> failedStates = new LinkedHashMap<>();
|
||
|
|
||
|
boolean mandatoryIntermediateStateHandled;
|
||
|
|
||
|
WalkStateGraphContext(Builder builder) {
|
||
|
initialStateClass = builder.initialStateClass;
|
||
|
finalStateClass = builder.finalStateClass;
|
||
|
mandatoryIntermediateState = builder.mandatoryIntermediateState;
|
||
|
loginContext = builder.loginContext;
|
||
|
}
|
||
|
|
||
|
public void recordWalkTo(State state) {
|
||
|
walkedStateGraphPath.add(state);
|
||
|
}
|
||
|
|
||
|
public boolean isWalksFinalState(StateDescriptor stateDescriptor) {
|
||
|
return stateDescriptor.getClass() == finalStateClass;
|
||
|
}
|
||
|
|
||
|
public boolean isFinalStateAuthenticatedAndResourceBound() {
|
||
|
return finalStateClass == AuthenticatedAndResourceBoundStateDescriptor.class;
|
||
|
}
|
||
|
|
||
|
public GraphVertex<State> maybeReturnMandatoryImmediateState(List<GraphVertex<State>> outgoingStateEdges) {
|
||
|
for (GraphVertex<State> outgoingStateVertex : outgoingStateEdges) {
|
||
|
if (outgoingStateVertex.getElement().getStateDescriptor().getClass() == mandatoryIntermediateState) {
|
||
|
mandatoryIntermediateStateHandled = true;
|
||
|
return outgoingStateVertex;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public List<State> getWalk() {
|
||
|
return CollectionUtil.newListWith(walkedStateGraphPath);
|
||
|
}
|
||
|
|
||
|
public int getWalkLength() {
|
||
|
return walkedStateGraphPath.size();
|
||
|
}
|
||
|
|
||
|
public void appendWalkTo(List<State> walk) {
|
||
|
walk.addAll(walkedStateGraphPath);
|
||
|
}
|
||
|
|
||
|
public LoginContext getLoginContext() {
|
||
|
return loginContext;
|
||
|
}
|
||
|
|
||
|
public boolean stateAlreadyVisited(State state) {
|
||
|
return walkedStateGraphPath.contains(state);
|
||
|
}
|
||
|
|
||
|
public void recordFailedState(State state, StateTransitionResult stateTransitionResult) {
|
||
|
failedStates.put(state, stateTransitionResult);
|
||
|
}
|
||
|
|
||
|
public Map<State, StateTransitionResult> getFailedStates() {
|
||
|
return new HashMap<>(failedStates);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the way to the final state via the given successor state that would loop, i.e., lead over the initial state and
|
||
|
* thus from a cycle.
|
||
|
*
|
||
|
* @param successorStateVertex the successor state to use on the way.
|
||
|
* @return <code>true</code> if it would loop, <code>false</code> otherwise.
|
||
|
*/
|
||
|
public boolean wouldCauseCycle(GraphVertex<State> successorStateVertex) {
|
||
|
Set<Class<? extends StateDescriptor>> visited = new HashSet<>();
|
||
|
return wouldCycleRecursive(successorStateVertex, visited);
|
||
|
}
|
||
|
|
||
|
private boolean wouldCycleRecursive(GraphVertex<State> stateVertex, Set<Class<? extends StateDescriptor>> visited) {
|
||
|
Class<? extends StateDescriptor> stateVertexClass = stateVertex.getElement().getStateDescriptor().getClass();
|
||
|
|
||
|
if (stateVertexClass == initialStateClass) {
|
||
|
return true;
|
||
|
}
|
||
|
if (finalStateClass == stateVertexClass || visited.contains(stateVertexClass)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
visited.add(stateVertexClass);
|
||
|
|
||
|
for (GraphVertex<State> successorStateVertex : stateVertex.getOutgoingEdges()) {
|
||
|
boolean cycle = wouldCycleRecursive(successorStateVertex, visited);
|
||
|
if (cycle) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public static Builder builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
|
||
|
return new Builder(initialStateClass, finalStateClass);
|
||
|
}
|
||
|
|
||
|
public static final class Builder {
|
||
|
private final Class<? extends StateDescriptor> initialStateClass;
|
||
|
private final Class<? extends StateDescriptor> finalStateClass;
|
||
|
private Class<? extends StateDescriptor> mandatoryIntermediateState;
|
||
|
private LoginContext loginContext;
|
||
|
|
||
|
private Builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
|
||
|
this.initialStateClass = Objects.requireNonNull(initialStateClass);
|
||
|
this.finalStateClass = Objects.requireNonNull(finalStateClass);
|
||
|
}
|
||
|
|
||
|
public Builder withMandatoryIntermediateState(Class<? extends StateDescriptor> mandatoryIntermedidateState) {
|
||
|
this.mandatoryIntermediateState = mandatoryIntermedidateState;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public Builder withLoginContext(String username, String password, Resourcepart resource) {
|
||
|
LoginContext loginContext = new LoginContext(username, password, resource);
|
||
|
return withLoginContext(loginContext);
|
||
|
}
|
||
|
|
||
|
public Builder withLoginContext(LoginContext loginContext) {
|
||
|
this.loginContext = loginContext;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public WalkStateGraphContext build() {
|
||
|
return new WalkStateGraphContext(this);
|
||
|
}
|
||
|
}
|
||
|
}
|