View Javadoc
1   package net.avcompris.commons3.api.tests;
2   
3   import static com.google.common.base.Preconditions.checkNotNull;
4   import static org.apache.commons.lang3.StringUtils.substringAfter;
5   import static org.apache.commons.lang3.StringUtils.substringBefore;
6   
7   import java.lang.reflect.Method;
8   
9   import javax.annotation.Nullable;
10  
11  import org.apache.commons.lang3.NotImplementedException;
12  import org.springframework.web.bind.annotation.RequestMapping;
13  
14  import net.avcompris.commons3.api.tests.TestsSpec.HttpMethod;
15  import net.avcompris.commons3.web.AbstractController;
16  
17  public abstract class ControllerContextUtils {
18  
19  	public static ControllerContext extractControllerContext( //
20  			final HttpMethod httpMethod, final String uri, //
21  			final Class<? extends AbstractController> controllerClass) {
22  
23  		checkNotNull(httpMethod, "httpMethod");
24  		checkNotNull(uri, "uri");
25  		checkNotNull(controllerClass, "controllerClass");
26  
27  		final String path;
28  		final String query;
29  
30  		if (uri.contains("?")) {
31  
32  			path = substringBefore(uri, "?");
33  			query = substringAfter(uri, "?");
34  
35  		} else {
36  
37  			path = uri;
38  			query = null;
39  		}
40  
41  		// 1. Exact matching
42  
43  		for (final Method method : controllerClass.getMethods()) {
44  
45  			final RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
46  
47  			if (requestMapping == null) {
48  				continue;
49  			} else if (!httpMethod.name().contentEquals(requestMapping.method()[0].name())) {
50  				continue;
51  			}
52  
53  			final String requestMappingPath = requestMapping.value()[0];
54  
55  			if (requestMappingPath.contains("{")) {
56  				continue;
57  			}
58  
59  			if (path.contentEquals(requestMappingPath)) {
60  
61  				final ControllerContext context = new ControllerContext( //
62  						controllerClass, //
63  						method);
64  
65  				return populateWithQuery(context, query);
66  			}
67  		}
68  
69  		// 2. Substitutions
70  
71  		methods: for (final Method method : controllerClass.getMethods()) {
72  
73  			final RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
74  
75  			if (requestMapping == null) {
76  				continue;
77  			} else if (!httpMethod.name().contentEquals(requestMapping.method()[0].name())) {
78  				continue;
79  			}
80  
81  			final String requestMappingPath = requestMapping.value()[0];
82  
83  			if (!requestMappingPath.contains("{")) {
84  				continue;
85  			}
86  
87  			final int start = requestMappingPath.indexOf("{");
88  
89  			if (!path.startsWith(requestMappingPath.substring(0, start))) {
90  				continue methods;
91  			}
92  
93  			int pathVariableOffset = start;
94  
95  			final ControllerContext context = new ControllerContext( //
96  					controllerClass, //
97  					method);
98  
99  			for (int variableOffset = start;;) {
100 
101 				final int variableEnd = requestMappingPath.indexOf("}", variableOffset) + 1;
102 
103 				final String suffix = requestMappingPath.substring(variableEnd);
104 
105 				final String variableName = requestMappingPath.substring(variableOffset + 1, variableEnd - 1);
106 
107 				if (!suffix.contains("{")) {
108 
109 					final String variableValue;
110 
111 					if (suffix.isEmpty()) {
112 
113 						variableValue = path.substring(pathVariableOffset);
114 
115 					} else if (path.length() >= variableOffset + suffix.length() && path.endsWith(suffix)) {
116 
117 						variableValue = path.substring(pathVariableOffset, path.length() - suffix.length());
118 
119 					} else {
120 
121 						continue methods;
122 					}
123 
124 					if (variableValue.contains("/")) {
125 
126 						continue methods;
127 					}
128 
129 					context.addVariable(variableName, variableValue);
130 
131 					break;
132 				}
133 
134 				variableOffset = requestMappingPath.indexOf("{", variableEnd);
135 
136 				final String fragment = requestMappingPath.substring(variableEnd, variableOffset);
137 
138 				if (fragment.isEmpty() //
139 						|| fragment.charAt(0) != '/' //
140 						|| fragment.charAt(fragment.length() - 1) != '/') {
141 
142 					throw new NotImplementedException("requestMappingPath: " + requestMappingPath);
143 				}
144 
145 				final String variableValue;
146 
147 				final int pathVariableEnd = path.indexOf("/", pathVariableOffset);
148 
149 				if (pathVariableEnd == -1) {
150 					continue methods;
151 				}
152 
153 				variableValue = path.substring(pathVariableOffset, pathVariableEnd);
154 
155 				pathVariableOffset = pathVariableEnd + variableOffset - variableEnd;
156 
157 				final String pathFragment = path.substring(pathVariableEnd, pathVariableOffset);
158 
159 				context.addVariable(variableName, variableValue);
160 
161 				if (!pathFragment.contentEquals(fragment)) {
162 
163 					continue methods;
164 				}
165 			}
166 
167 			return context;
168 		}
169 
170 		// 9. Nothing matches
171 
172 		return null;
173 	}
174 
175 	private static ControllerContext populateWithQuery(final ControllerContext context, @Nullable final String query) {
176 
177 		if (query == null) {
178 
179 			return context;
180 		}
181 
182 		if (query.contains("&")) {
183 			throw new NotImplementedException("query: " + query);
184 		}
185 
186 		final String variableName = substringBefore(query, "=");
187 		final String variableValue = substringAfter(query, "=").replace('+', ' ');
188 
189 		context.addVariable(variableName, variableValue);
190 
191 		return context;
192 	}
193 }