Created
December 21, 2018 20:34
-
-
Save rbonatuvic/d3ef9e8dc0c5a78870a8520bc2ab2b74 to your computer and use it in GitHub Desktop.
Formatter for spring webflow, org.springframework.webflow.engine.Flow, toString output and unit test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ca.uvic.idm.cas.web.flow; | |
import lombok.extern.slf4j.Slf4j; | |
import lombok.val; | |
import org.apereo.cas.configuration.CasConfigurationProperties; | |
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer; | |
import org.springframework.context.ApplicationContext; | |
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; | |
import org.springframework.webflow.engine.ActionState; | |
import org.springframework.webflow.engine.Flow; | |
import org.springframework.webflow.engine.builder.support.FlowBuilderServices; | |
import java.io.BufferedWriter; | |
import java.io.FileWriter; | |
import java.io.IOException; | |
import java.util.regex.Pattern; | |
@Slf4j | |
public abstract class AbstractUvicCasWebflowConfigurer extends AbstractCasWebflowConfigurer { | |
protected String outputFileName = "/tmp/flow.txt"; | |
public AbstractUvicCasWebflowConfigurer(FlowBuilderServices flowBuilderServices, | |
FlowDefinitionRegistry loginFlowDefinitionRegistry, | |
ApplicationContext applicationContext, | |
CasConfigurationProperties casProperties) { | |
super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties); | |
} | |
/** | |
* Inserts an inbound state into a transition for a target state and sets inbound | |
* state's matching transition to the previous target (preserving overall flow). | |
* Inbound state inserts itself into target state by replacing target transition. | |
* @param flow | |
* @param inboundStateId bean identifier for insertable action state | |
* @param inboundActionId action to be performed | |
* @param targetStateid bean identifier for state immediately before inbound | |
* @param targetTransitionId transition point for insertion | |
*/ | |
protected ActionState insertIntoFlow( | |
final Flow flow, | |
final String inboundStateId, | |
final String inboundActionId, | |
final String targetStateid, | |
final String targetTransitionId) { | |
val inboundActionState = createActionState(flow, inboundStateId, inboundActionId); | |
val targetState = getState(flow, targetStateid, ActionState.class); | |
val destinationStateId = targetState.getTransition(targetTransitionId).getTargetStateId(); | |
val inboundTransitionSet = inboundActionState.getTransitionSet(); | |
inboundTransitionSet.add(createTransition(targetTransitionId, destinationStateId)); | |
createTransitionForState(targetState, targetTransitionId, inboundStateId, true); | |
if (LOGGER.isTraceEnabled()) { | |
flowToFile(flow); | |
} | |
return inboundActionState; | |
} | |
protected void flowToFile(final Flow flow) { | |
flowToFile(flow, outputFileName); | |
} | |
protected void flowToFile(final Flow flow, final String fileName) { | |
String s = flow.toString().trim(); | |
String formatted = formatFlow(s); | |
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) { | |
writer.write(s); | |
writer.write("\n\n\n\n"); | |
writer.write(formatted); | |
} catch (IOException e) { | |
LOGGER.error(e.getMessage()); | |
} | |
} | |
/** | |
* Formats a spring webflow flow to help determine how to modify a flow. | |
* Adds new lines and indents to make it easier to read. | |
* @param input flow.toString() | |
* @return nicely formatted flow | |
*/ | |
public String formatFlow(final String input) { | |
//LOGGER.debug("input: ." + input + "."); | |
// used to add an extra indent for an object's field members | |
java.util.Stack<java.util.AbstractMap.SimpleEntry> stack = new java.util.Stack<>(); | |
int currPosition = 0; | |
String indent = ""; | |
String indentor = "\t"; | |
String newLine = "\n"; | |
// object identifier | |
java.util.regex.Pattern objPattern = Pattern.compile("^(\\w+@\\w+)\\b.*"); | |
String in = input.trim(); | |
StringBuilder out = new StringBuilder(); | |
while (in.length() > currPosition) { | |
java.util.regex.Matcher m = objPattern.matcher(in.substring(currPosition)); | |
String firstTwo = ""; | |
// capture first two characters to match against ']' or '],' | |
if (1 < in.length() - currPosition) { | |
firstTwo = in.substring(currPosition, currPosition + 2); | |
} else { | |
// at end of input | |
firstTwo = in.substring(currPosition, currPosition + 1); | |
} | |
if (in.startsWith("[", currPosition)) { | |
out.append(indent).append(in.charAt(currPosition)).append(newLine); | |
indent += indentor; | |
currPosition++; | |
if (!stack.empty()) { | |
java.util.AbstractMap.SimpleEntry<String, Integer> se = stack.pop(); | |
se.setValue(se.getValue() + 1); | |
stack.push(se); | |
} | |
} else if (firstTwo.startsWith("]")) { | |
if (!stack.empty()) { | |
java.util.AbstractMap.SimpleEntry<String, Integer> se = stack.pop(); | |
if (1 > se.getValue()) { | |
// outdent after printing member variables | |
indent = indent.replaceFirst(indentor, ""); | |
if (!stack.empty()) { | |
// this ] closes from outer object | |
java.util.AbstractMap.SimpleEntry<String, Integer> seOuter = stack.pop(); | |
seOuter.setValue(seOuter.getValue() - 1); | |
stack.push(seOuter); | |
} | |
} else { | |
se.setValue(se.getValue() - 1); | |
stack.push(se); | |
} | |
} | |
indent = indent.replaceFirst(indentor, ""); | |
out.append(indent).append("]"); | |
if ("],".equals(firstTwo)) { | |
out.append(","); | |
currPosition++; | |
} | |
out.append(newLine); | |
currPosition++; | |
} else if (m.matches()) { | |
String obj = m.group(1); | |
out.append(indent).append(obj).append(newLine); | |
indent = indent + indentor; | |
// prepare for members | |
stack.push(new java.util.AbstractMap.SimpleEntry<String, Integer>(obj, 0)); | |
currPosition += obj.length(); | |
} else { | |
int nextOpenBracket = in.indexOf("[", currPosition); | |
int nextCloseBracket = in.indexOf("]", currPosition); | |
int nextComma = in.indexOf(",", currPosition); | |
int nextMark = 0; | |
boolean increaseIndent = false; | |
// if [ or , not found, push beyond last position which would be ] | |
if (0 > nextOpenBracket) { | |
nextOpenBracket = in.length(); | |
} | |
if (0 > nextComma) { | |
nextComma = in.length(); | |
} | |
// add 1 when [ and , since they should remain on same line and ] should be on next line | |
if (nextCloseBracket > nextOpenBracket) { | |
if (nextOpenBracket > nextComma) { | |
nextMark = nextComma + 1; | |
} else { | |
nextMark = nextOpenBracket + 1; | |
// bypass empty and null | |
if ((in.substring(nextMark).startsWith("[empty]]")) | |
|| (in.substring(nextMark).startsWith("null]"))) { | |
if (in.substring(nextMark).startsWith("[empty]],")) { | |
nextMark += 9; | |
} else if (in.substring(nextMark).startsWith("[empty]]")) { | |
nextMark += 8; | |
} else if (in.substring(nextMark).startsWith("null],")) { | |
nextMark += 6; | |
} else if (in.substring(nextMark).startsWith("null]")) { | |
nextMark += 5; | |
} | |
} else { | |
// indent members | |
increaseIndent = true; | |
if (!stack.empty()) { | |
java.util.AbstractMap.SimpleEntry<String, Integer> se = stack.pop(); | |
se.setValue(se.getValue() + 1); | |
stack.push(se); | |
} | |
} | |
} | |
} else if (nextCloseBracket > nextComma) { | |
nextMark = nextComma + 1; | |
} else { | |
nextMark = nextCloseBracket; | |
} | |
String s = in.substring(currPosition, nextMark).trim(); | |
if (0 < s.length()) { | |
out.append(indent).append(s).append(newLine); | |
currPosition = nextMark; | |
} | |
if (increaseIndent) { | |
// for next line | |
indent = indent + indentor; | |
} | |
} | |
} | |
String formatted = out.toString().trim(); | |
//LOGGER.debug("formatted: ." + formatted + "."); | |
return formatted; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment