Java tutorial
package net.eusashead.hateoas.hal.response.impl; /* * #[license] * spring-halbuilder * %% * Copyright (C) 2013 Eusa's Head * %% * 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. * %[license] */ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import net.eusashead.hateoas.hal.adapter.RepresentationWriter; import net.eusashead.hateoas.hal.response.HalPageResponseBuilder; import net.eusashead.hateoas.header.ETagHeaderStrategy; import net.eusashead.hateoas.response.ResponseBuilder; import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import com.theoryinpractise.halbuilder.api.ReadableRepresentation; import com.theoryinpractise.halbuilder.api.Representation; import com.theoryinpractise.halbuilder.api.RepresentationFactory; /** * {@link ResponseBuilder} used for * creating a HAL {@link Representation} that * is a "page" of data - a subset of a result * set from a database used to conserve bandwidth * * The main benefit of this class is that automates * the construction of "next" and "previous" links * within the HAL {@link Representation} * * @author patrickvk * */ public class HalPageResponseBuilderImpl extends AbstractHalResponseBuilder implements HalPageResponseBuilder { private static final String CONTENT_REL_NAME = "content"; /** * The name of the page paramter in the request query string */ private String pageParameter = "page"; /** * The underlying Page */ private Page<?> page; /** * Construct with default values * for page size and page query * parameter name * * @param representationFactory {@link RepresentationFactory} for constructing Hal {@link Representation} * @param request {@link HttpServletRequest} the incoming request from which URI and page parameter are to be extracted */ public HalPageResponseBuilderImpl(RepresentationFactory representationFactory, HttpServletRequest request) { super(representationFactory, request); } /** * Construct with supplied values * for page size and page query * parameter name * * @param representationFactory {@link RepresentationFactory} for constructing Hal {@link Representation} * @param request {@link HttpServletRequest} the incoming request from which URI and page parameter are to be extracted * @param pageParameter {@link String} name of the page number request query parameter */ public HalPageResponseBuilderImpl(RepresentationFactory representationFactory, HttpServletRequest request, String pageParameter) { super(representationFactory, request); this.pageParameter = pageParameter; } @Override public <T> HalPageResponseBuilder page(Page<T> content) { this.page = content; this.assertPage(); super.assertEntity(); for (T item : safeList(content)) { this.entity.withBeanBasedRepresentation(CONTENT_REL_NAME, null, item); } return this; } @Override public <T> HalPageResponseBuilderImpl page(Page<T> content, RepresentationWriter<T> representationWriter) { this.page = content; this.assertPage(); super.assertEntity(); if (representationWriter == null) { throw new IllegalArgumentException("Parameter representationWriter cannot be null."); } for (T item : safeList(content)) { ReadableRepresentation rep = representationWriter.write(item, representationFactory); this.entity.withRepresentation(CONTENT_REL_NAME, rep); } return this; } private <T> Iterable<T> safeList(Iterable<T> iterable) { return iterable == null ? Collections.<T>emptyList() : iterable; } @Override public HalPageResponseBuilderImpl etag() { super.setEtagHeader(); return this; } @Override public HalPageResponseBuilderImpl etag(ETagHeaderStrategy strategy) { super.setEtagHeader(strategy); return this; } @Override public HalPageResponseBuilderImpl etag(Date date) { super.setEtagHeader(date); return this; } @Override public HalPageResponseBuilderImpl etag(Integer version) { super.setEtagHeader(version); return this; } @Override public HalPageResponseBuilderImpl lastModified(Date date) { super.setLastModifiedHeader(date); return this; } @Override public HalPageResponseBuilderImpl expireIn(long millis) { super.setExpiryHeaders(millis); return this; } @Override public ResponseEntity<Representation> build() { // Check the representation is valid super.assertEntity(); // Check that the underlying page is set this.assertPage(); // Extract page data such as size, page number entity.withProperty("size", this.page.getSize()); entity.withProperty("page", this.page.getNumber()); entity.withProperty("numberOfElements", this.page.getNumberOfElements()); entity.withProperty("totalElements", this.page.getTotalElements()); // Next/back links if (this.page.hasNextPage()) { this.buildNextLink(); } if (this.page.hasPreviousPage()) { this.buildPreviousLink(); } // Assemble the response return super.buildResponseEntity(this.entity); } private void assertPage() { if (this.page == null) { throw new RuntimeException("Page cannot be null."); } } /** * Create the "next" link * for the paged search results */ private void buildNextLink() { Map<String, String[]> params = modifyPageNumber(request, 1); String link = buildLink(params); entity.withLink("next", link); } /** * Create the "previous" link * for the paged search results */ private void buildPreviousLink() { Map<String, String[]> params = modifyPageNumber(request, -1); String link = buildLink(params); entity.withLink("previous", link); } /** * Create a link using the URI and * query string parameters * @param params * @return */ private String buildLink(Map<String, String[]> params) { String queryString = buildQueryString(params); String link = String.format("%s?%s", request.getRequestURI(), queryString); return link; } /** * Build a query string from the supplied parameters * @param params * @return */ private String buildQueryString(Map<String, String[]> params) { // Build the query string up from the parameters StringBuilder builder = new StringBuilder(); for (String key : params.keySet()) { for (String val : params.get(key)) { builder.append(key); builder.append("="); builder.append(val); builder.append("&"); } } // Remove the last ampersand if it is the trailing character if (builder.length() > 0) { if (builder.lastIndexOf("&") == (builder.length() - 1)) { builder.deleteCharAt(builder.length() - 1); } } return builder.toString(); } /** * Get the existing request * parameters and increment * the page parameter (or decrement). * * The request should be identical except * for the page parameter * @param request * @param modifier * @return */ private Map<String, String[]> modifyPageNumber(HttpServletRequest request, int modifier) { Map<String, String[]> oldParams = request.getParameterMap(); Map<String, String[]> newParams = new HashMap<String, String[]>(); // Set all parameters other than page number for (String key : oldParams.keySet()) { String[] value = oldParams.get(key); if (!key.equals(pageParameter)) { newParams.put(key, value); } } // Set page number int page = this.page.getNumber() + modifier; newParams.put(pageParameter, new String[] { Integer.valueOf(page).toString() }); return newParams; } }