in grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/DefaultUrlMappingEvaluator.java [586:733]
private Object _invoke(String methodName, Object arg, Object delegate) {
try {
var mappingInfo = pushNewMetaMappingInfo();
var currentConstraints = mappingInfo.getConstraints();
var args = (Object[]) arg;
var mappedURI = establishFullURI(methodName);
var isResponseCode = isResponseCode(mappedURI);
if (mappedURI.startsWith(SLASH) || isResponseCode) {
// Create a new parameter map for this mapping.
parameterValues = new HashMap<>();
Map<?,?> variables = binding != null ? binding.getVariables() : null;
boolean hasParent = !parentResources.isEmpty();
try {
if (!hasParent) {
urlDefiningMode = false;
}
args = args != null && args.length > 0 ? args : new Object[] {Collections.emptyMap()};
if (args[0] instanceof Closure<?>) {
UrlMappingData urlData = createUrlMappingData(mappedURI, isResponseCode);
Closure<?> callable = (Closure<?>) args[0];
if (delegate != null) {
callable.setDelegate(delegate);
}
callable.call();
if (binding != null) {
mappingInfo.setController(variables.get(CONTROLLER));
mappingInfo.setAction(variables.get(ACTION));
mappingInfo.setView(variables.get(VIEW));
mappingInfo.setUri(variables.get(UrlMapping.URI));
mappingInfo.setPlugin(variables.get(PLUGIN));
mappingInfo.setNamespace(variables.get(NAMESPACE));
if (variables.containsKey(HTTP_METHOD)) {
mappingInfo.setHttpMethod(variables.get(HTTP_METHOD).toString());
}
}
var constraints = currentConstraints.toArray(new ConstrainedProperty[0]);
UrlMapping urlMapping;
if (mappingInfo.getUri() != null) {
try {
urlMapping = new RegexUrlMapping(urlData, new URI(mappingInfo.getUri().toString()), constraints, grailsApplication);
} catch (URISyntaxException e) {
throw new UrlMappingException("Cannot map to invalid URI: " + e.getMessage(), e);
}
} else {
urlMapping = createURLMapping(urlData, isResponseCode, mappingInfo.getRedirectInfo(), mappingInfo.getController(), mappingInfo.getAction(), mappingInfo.getNamespace(), mappingInfo.getPlugin(), mappingInfo.getView(), mappingInfo.getHttpMethod(), null, constraints);
}
if (binding != null) {
Object parse = getParseRequest(Collections.emptyMap(), variables);
if (parse instanceof Boolean) {
urlMapping.setParseRequest((Boolean) parse);
}
}
configureUrlMapping(urlMapping);
return urlMapping;
}
if (args[0] instanceof Map) {
Map<?,?> namedArguments = (Map<?,?>) args[0];
String version = null;
if (namedArguments.containsKey(UrlMapping.VERSION)) {
version = namedArguments.get(UrlMapping.VERSION).toString();
}
if (namedArguments.containsKey(NAMESPACE)) {
mappingInfo.setNamespace(namedArguments.get(NAMESPACE).toString());
}
if (namedArguments.containsKey(PLUGIN)) {
mappingInfo.setPlugin(namedArguments.get(PLUGIN).toString());
}
var urlData = createUrlMappingData(mappedURI, isResponseCode);
if (namedArguments.containsKey(RESOURCE) || namedArguments.containsKey(SINGLE)) {
Object controller;
if (namedArguments.containsKey(RESOURCE)) {
GrailsUtil.deprecated("The " + RESOURCE + " syntax is deprecated and will be removed in a future release. Use " + SINGLE + " instead.");
controller = namedArguments.get(RESOURCE);
} else {
controller = namedArguments.get(SINGLE);
}
var controllerName = controller.toString();
mappingInfo.setController(controllerName);
parentResources.push(new ParentResource(controllerName, mappedURI, true));
try {
invokeLastArgumentIfClosure(args);
} finally {
parentResources.pop();
}
createSingleResourceRestfulMappings(controllerName, mappingInfo.getPlugin(), mappingInfo.getNamespace(), version, urlData, currentConstraints, calculateIncludes(namedArguments, DEFAULT_RESOURCE_INCLUDES));
} else if (namedArguments.containsKey(RESOURCES)) {
var controller = namedArguments.get(RESOURCES);
var controllerName = controller.toString();
mappingInfo.setController(controllerName);
parentResources.push(new ParentResource(controllerName, mappedURI, false));
try {
urlDefiningMode = true;
invokeLastArgumentIfClosure(args);
} finally {
parentResources.pop();
hasParent = !parentResources.isEmpty();
if (!hasParent) {
urlDefiningMode = false;
}
}
createResourceRestfulMappings(controllerName, mappingInfo.getPlugin(), mappingInfo.getNamespace(), version, urlData, currentConstraints, calculateIncludes(namedArguments, DEFAULT_RESOURCES_INCLUDES));
}
else {
invokeLastArgumentIfClosure(args);
var urlMapping = getURLMappingForNamedArgs(namedArguments, urlData, mappedURI, isResponseCode, currentConstraints);
configureUrlMapping(urlMapping);
return urlMapping;
}
}
return null;
} finally {
if (binding != null && variables != null) {
variables.clear();
}
if (!hasParent) {
urlDefiningMode = true;
}
}
} else if ((!urlDefiningMode || (!parentResources.isEmpty() && parentResources.peek().isGroup)) && CONSTRAINTS.equals(mappedURI)) {
if (args.length > 0 && (args[0] instanceof Closure<?>)) {
Closure<?> callable = (Closure<?>) args[0];
var builder = constraintsEvaluator.newConstrainedPropertyBuilder(UrlMapping.class);
for (var constrainedProperty : currentConstraints) {
builder.getConstrainedProperties().put(constrainedProperty.getPropertyName(), constrainedProperty);
}
callable.setResolveStrategy(Closure.DELEGATE_FIRST);
callable.setDelegate(builder);
callable.call();
return builder.getConstrainedProperties();
}
return Collections.emptyMap();
} else {
log.error("Mapping: '{}' does not start with {} or is response code.", mappedURI, SLASH);
return super.invokeMethod(mappedURI, arg);
}
} finally {
mappingInfoDeque.pop();
}
}