Java tutorial
/* * Copyright 2008-2010 the original author or authors. * * 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 org.synyx.hades.dao.query; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.springframework.util.Assert; import org.synyx.hades.dao.Param; import org.synyx.hades.domain.Pageable; import org.synyx.hades.domain.Sort; /** * Abstracts method parameters that have to be bound to query parameters or * applied to the query independently. * * @author Oliver Gierke */ final class Parameters implements Iterable<Parameter> { @SuppressWarnings("unchecked") static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class); private static final String ALL_OR_NOTHING = String.format( "Either use @%s " + "on all parameters except %s and %s typed once, or none at all!", Param.class.getSimpleName(), Pageable.class.getSimpleName(), Sort.class.getSimpleName()); private final int pageableIndex; private final int sortIndex; private final List<Parameter> parameters; /** * Creates a new instance of {@link Parameters}. * * @param method */ public Parameters(Method method) { Assert.notNull(method); this.parameters = new ArrayList<Parameter>(); List<Class<?>> types = Arrays.asList(method.getParameterTypes()); Annotation[][] annotations = method.getParameterAnnotations(); for (int i = 0; i < types.size(); i++) { parameters.add(new Parameter(types.get(i), annotations[i], this, i)); } this.pageableIndex = types.indexOf(Pageable.class); this.sortIndex = types.indexOf(Sort.class); assertEitherAllParamAnnotatedOrNone(); } /** * Creates a new {@link Parameters} instance with the given * {@link Parameter}s put into new context. * * @param originals */ private Parameters(List<Parameter> originals) { this.parameters = new ArrayList<Parameter>(); int pageableIndexTemp = -1; int sortIndexTemp = -1; for (int i = 0; i < originals.size(); i++) { Parameter original = originals.get(i); this.parameters.add(new Parameter(original, this, i)); pageableIndexTemp = original.isPageable() ? i : -1; sortIndexTemp = original.isSort() ? i : -1; } this.pageableIndex = pageableIndexTemp; this.sortIndex = sortIndexTemp; } /** * Returns whether the method the {@link Parameters} was created for * contains a {@link Pageable} argument. This does not include a check if an * actual parameter was provided, so {@link #getPageable()} might return * {@literal null} even in case this method returns {@literal true}. * * @return */ public boolean hasPageableParameter() { return pageableIndex != -1; } /** * Returns the index of the {@link Pageable} {@link Method} parameter if * available. Will return {@literal -1} if there is no {@link Pageable} * argument in the {@link Method}'s parameter list. * * @return the pageableIndex */ public int getPageableIndex() { return pageableIndex; } /** * Returns the index of the {@link Sort} {@link Method} parameter if * available. Will return {@literal -1} if there is no {@link Sort} argument * in the {@link Method}'s parameter list. * * @return */ public int getSortIndex() { return sortIndex; } /** * Returns whether the method the {@link Parameters} was created for * contains a {@link Sort} argument. This does not include a check if an * actual parameter was provided, so {@link #getSort()} might return * {@literal null} even in case this method returns {@literal true}. * * @return */ public boolean hasSortParameter() { return sortIndex != -1; } public Parameter getParameter(int index) { try { return parameters.get(index); } catch (IndexOutOfBoundsException e) { throw new ParameterOutOfBoundsException(e); } } /** * Returns whether we have a parameter at the given position. * * @param position * @return */ public boolean hasParameterAt(int position) { try { return null != getParameter(position); } catch (ParameterOutOfBoundsException e) { return false; } } /** * Returns whether the method signature contains one of the special * parameters ({@link Pageable}, {@link Sort}). * * @return */ public boolean hasSpecialParameter() { return hasSortParameter() || hasPageableParameter(); } /** * Returns the number of parameters. * * @return */ public int getNumberOfParameters() { return parameters.size(); } /** * Returns a {@link Parameters} instance with effectively all special * parameters removed. * * @see Parameter#TYPES * @see Parameter#isSpecialParameter() * @return */ public Parameters getBindableParameters() { List<Parameter> bindables = new ArrayList<Parameter>(); for (Parameter candidate : this) { if (candidate.isBindable()) { bindables.add(candidate); } } return new Parameters(bindables); } /** * Returns the index of the placeholder inside a query for the parameter * with the given index. They might differ from the parameter index as the * method signature can contain special parameters (e.g. {@link Sort}, * {@link Pageable}) that are not bound as plain query parameters but rather * handled differently. * * @param index * @return the placeholder postion for the parameter with the given index. * Will return 0 for special parameters. */ int getPlaceholderPosition(Parameter parameter) { return parameter.isSpecialParameter() ? 0 : getPlaceholderPositionRecursively(parameter); } private int getPlaceholderPositionRecursively(Parameter parameter) { int result = parameter.isSpecialParameter() ? 0 : 1; return parameter.isFirst() ? result : result + getPlaceholderPositionRecursively(parameter.getPrevious()); } /** * Asserts that either all of the non special parameters ({@link Pageable}, * {@link Sort}) are annotated with {@link Param} or none of them is. * * @param method */ private void assertEitherAllParamAnnotatedOrNone() { boolean annotationFound = false; for (Parameter parameter : this.getBindableParameters()) { if (parameter.isNamedParameter()) { Assert.isTrue(annotationFound || parameter.isFirst(), ALL_OR_NOTHING); annotationFound = true; } else { Assert.isTrue(!annotationFound, ALL_OR_NOTHING); } } } /** * Returns whether the given type is a bindable parameter. * * @param type * @return */ public static boolean isBindable(Class<?> type) { return !TYPES.contains(type); } /* * (non-Javadoc) * * @see java.lang.Iterable#iterator() */ public Iterator<Parameter> iterator() { return parameters.iterator(); } }