/* * Copyright 2011-2017 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.springframework.data.mongodb.repository.query; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Sort; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.convert.MongoWriter; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import com.mongodb.DBRef; /** * Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format. * * @author Oliver Gierke * @author Christoph Strobl * @author Thomas Darimont * @author Mark Paluch */ public class ConvertingParameterAccessor implements MongoParameterAccessor { private final MongoWriter<?> writer; private final MongoParameterAccessor delegate; /** * Creates a new {@link ConvertingParameterAccessor} with the given {@link MongoWriter} and delegate. * * @param writer must not be {@literal null}. * @param delegate must not be {@literal null}. */ public ConvertingParameterAccessor(MongoWriter<?> writer, MongoParameterAccessor delegate) { Assert.notNull(writer, "MongoWriter must not be null!"); Assert.notNull(delegate, "MongoParameterAccessor must not be null!"); this.writer = writer; this.delegate = delegate; } /* * (non-Javadoc) * * @see java.lang.Iterable#iterator() */ public PotentiallyConvertingIterator iterator() { return new ConvertingIterator(delegate.iterator()); } /* * (non-Javadoc) * * @see org.springframework.data.repository.query.ParameterAccessor#getPageable() */ public Pageable getPageable() { return delegate.getPageable(); } /* * (non-Javadoc) * * @see org.springframework.data.repository.query.ParameterAccessor#getSort() */ public Sort getSort() { return delegate.getSort(); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.ParameterAccessor#getDynamicProjection() */ @Override public Optional<Class<?>> getDynamicProjection() { return delegate.getDynamicProjection(); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int) */ public Object getBindableValue(int index) { return getConvertedValue(delegate.getBindableValue(index), null); } /* * (non-Javadoc) * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getDistanceRange() */ @Override public Range<Distance> getDistanceRange() { return delegate.getDistanceRange(); } /* * (non-Javadoc) * @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation() */ public Point getGeoNearLocation() { return delegate.getGeoNearLocation(); } /* * (non-Javadoc) * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getFullText() */ public TextCriteria getFullText() { return delegate.getFullText(); } /** * Converts the given value with the underlying {@link MongoWriter}. * * @param value can be {@literal null}. * @param typeInformation can be {@literal null}. * @return */ private Object getConvertedValue(Object value, TypeInformation<?> typeInformation) { return writer.convertToMongoType(value, typeInformation == null ? null : typeInformation.getActualType()); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.ParameterAccessor#hasBindableNullValue() */ public boolean hasBindableNullValue() { return delegate.hasBindableNullValue(); } /** * Custom {@link Iterator} to convert items before returning them. * * @author Oliver Gierke */ private class ConvertingIterator implements PotentiallyConvertingIterator { private final Iterator<Object> delegate; /** * Creates a new {@link ConvertingIterator} for the given delegate. * * @param delegate */ public ConvertingIterator(Iterator<Object> delegate) { this.delegate = delegate; } /* * (non-Javadoc) * @see java.util.Iterator#hasNext() */ public boolean hasNext() { return delegate.hasNext(); } /* * (non-Javadoc) * @see java.util.Iterator#next() */ public Object next() { return delegate.next(); } /* * (non-Javadoc) * @see org.springframework.data.mongodb.repository.ConvertingParameterAccessor.PotentiallConvertingIterator#nextConverted() */ public Object nextConverted(MongoPersistentProperty property) { Object next = next(); if (next == null) { return null; } if (property.isAssociation()) { if (next.getClass().isArray() || next instanceof Iterable) { List<DBRef> dbRefs = new ArrayList<DBRef>(); for (Object element : asCollection(next)) { dbRefs.add(writer.toDBRef(element, property)); } return dbRefs; } else { return writer.toDBRef(next, property); } } return getConvertedValue(next, property.getTypeInformation()); } /* * (non-Javadoc) * @see java.util.Iterator#remove() */ public void remove() { delegate.remove(); } } /** * Returns the given object as {@link Collection}. Will do a copy of it if it implements {@link Iterable} or is an * array. Will return an empty {@link Collection} in case {@literal null} is given. Will wrap all other types into a * single-element collection. * * @param source * @return */ private static Collection<?> asCollection(Object source) { if (source instanceof Iterable) { List<Object> result = new ArrayList<Object>(); for (Object element : (Iterable<?>) source) { result.add(element); } return result; } if (source == null) { return Collections.emptySet(); } return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source); } /* * (non-Javadoc) * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getValues() */ @Override public Object[] getValues() { return delegate.getValues(); } /** * Custom {@link Iterator} that adds a method to access elements in a converted manner. * * @author Oliver Gierke */ public interface PotentiallyConvertingIterator extends Iterator<Object> { /** * Returns the next element which has already been converted. * * @return */ Object nextConverted(MongoPersistentProperty property); } }