Java tutorial
/* * Copyright 2012 Guido Steinacker * * 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 de.otto.jsonhome.generator; import de.otto.jsonhome.annotation.Href; import de.otto.jsonhome.annotation.HrefTemplate; import de.otto.jsonhome.annotation.Rel; import de.otto.jsonhome.model.Hints; import de.otto.jsonhome.model.ResourceLink; import java.lang.reflect.Method; import java.net.URI; import java.util.List; import static de.otto.jsonhome.generator.MethodHelper.getParameterInfos; import static de.otto.jsonhome.generator.UriBuilder.normalized; import static de.otto.jsonhome.model.DirectLink.directLink; import static de.otto.jsonhome.model.TemplatedLink.templatedLink; import static java.net.URI.create; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; /** * A generator used to generate the {@link ResourceLink resource links} of a method. * * @author Guido Steinacker * @since 17.10.12 */ public abstract class ResourceLinkGenerator { private final URI applicationBaseUri; private final URI relationTypeBaseUri; private final URI varTypeBaseUri; private final HintsGenerator hintsGenerator; private final HrefVarsGenerator hrefVarsGenerator; /** * Generator used to generate {@link ResourceLink} instances. * * @param applicationBaseUri base URI of the application. This is used to generate HREFs and HREF-TEMPLATES. * @param relationTypeBaseUri base URI of all link-relation types. * @param varTypeBaseUri base URI used to generate var-types, identifying the semantics of variables used in href-templates. * @param hintsGenerator generator used to create {@link Hints} * @param hrefVarsGenerator generator used to create {@link de.otto.jsonhome.model.HrefVar HrefVars}. */ protected ResourceLinkGenerator(final URI applicationBaseUri, final URI relationTypeBaseUri, final URI varTypeBaseUri, final HintsGenerator hintsGenerator, final HrefVarsGenerator hrefVarsGenerator) { this.applicationBaseUri = normalized(applicationBaseUri).toUri(); this.relationTypeBaseUri = normalized(relationTypeBaseUri).toUri(); this.varTypeBaseUri = varTypeBaseUri != null ? normalized(varTypeBaseUri).toUri() : null; this.hintsGenerator = hintsGenerator; this.hrefVarsGenerator = hrefVarsGenerator; } /** * Analyzes a method of a controller and returns the list of ResourceLinks of this method. * * @param method the method * @return resource link or null. */ public ResourceLink resourceLinkFor(final Method method) { ResourceLink resourceLink = null; if (isCandidateForAnalysis(method)) { final String resourcePath = overriddenOrCalculatedResourcePathFor(method); final URI relationType = relationTypeFrom(method); final URI varTypeBaseUri = this.varTypeBaseUri != null ? this.varTypeBaseUri : relationType; if (relationType != null) { final Hints hints = hintsGenerator.hintsOf(relationType, method); if (resourcePath.matches(".*\\{.*\\}.*")) { resourceLink = templatedLink(relationType, resourcePath, hrefVarsGenerator.hrefVarsFor(varTypeBaseUri, this.varTypeBaseUri == null, method), hints); } else { resourceLink = directLink(relationType, create(resourcePath), hints); } } } return resourceLink; } /** * Calculates the resource path (direct links or templated links) of the method. * * @param method the Method * @return resource path or null. */ protected String overriddenOrCalculatedResourcePathFor(final Method method) { final Href methodHrefAnnotation = findAnnotation(method, Href.class); if (methodHrefAnnotation != null) { return applicationBaseUri.resolve(methodHrefAnnotation.value()).toString(); } else { final HrefTemplate hrefTemplateAnnotation = findAnnotation(method, HrefTemplate.class); if (hrefTemplateAnnotation != null) { return hrefTemplateAnnotation.value().startsWith("http://") ? hrefTemplateAnnotation.value() : normalized(applicationBaseUri).withPathSegment(hrefTemplateAnnotation.value()).toString(); } else { return resourcePathFor(method); } } } /** * Parses the method parameter annotations, looking for parameters annotated as @RequestParam, and returns the * query part of a href-template. * * @param method method, optionally having parameters annotated as being a @RequestParam. * @return query part of a href-template, like {?param1,param2,param3} */ protected String queryTemplateFrom(final Method method) { final StringBuilder sb = new StringBuilder(); final List<ParameterInfo> parameterInfos = getParameterInfos(method); boolean first = true; for (final ParameterInfo parameterInfo : parameterInfos) { if (hrefVarsGenerator.hasRequestParam(parameterInfo)) { if (first) { first = false; sb.append("{?").append(parameterInfo.getName()); } else { sb.append(",").append(parameterInfo.getName()); } } } final String s = sb.toString(); return s.isEmpty() ? s : s + "}"; } /** * Analyses a method of a controller and returns the fully qualified URI of the link-relation type. * * If the neither the method, nor the controller is annotated with Rel, null is returned. * * The Rel of the method is overriding the Rel of the Controller. * * @param method the method * @return URI of the link-relation type, or null */ protected URI relationTypeFrom(final Method method) { final Rel controllerRel = findAnnotation(method.getDeclaringClass(), Rel.class); final Rel methodRel = findAnnotation(method, Rel.class); if (controllerRel == null && methodRel == null) { return null; } else { final String linkRelationType = methodRel != null ? methodRel.value() : controllerRel.value(); if (linkRelationType.startsWith("http://")) { return create(linkRelationType); } else { return normalized(relationTypeBaseUri).withPathSegment(linkRelationType).toUri(); } } } /** * Returns true if the method is a candidate for further processing, false otherwise. * * @param method the method to check * @return boolean */ protected abstract boolean isCandidateForAnalysis(final Method method); /** * Returns the resource path for the given method. * <p/> * If the method is able to handle multiple URIs, the implementation should select the first or * best URI. * * @param method the method of the controller, possibly handling one or more REST resources. * @return list of resource paths */ protected abstract String resourcePathFor(final Method method); }