/* * Copyright 2015-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.rest.core.mapping; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Optional; import java.util.Set; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * {@link ResourceMappings} for {@link PersistentEntities}. * * @author Oliver Gierke * @author Mark Paluch */ public class PersistentEntitiesResourceMappings implements ResourceMappings { private final PersistentEntities entities; private final SearchResourceMappings searchResourceMappings = new SearchResourceMappings( Collections.<MethodResourceMapping> emptyList()); private final Map<Class<?>, ResourceMetadata> cache = new HashMap<Class<?>, ResourceMetadata>(); private final Map<Class<?>, MappingResourceMetadata> mappingCache = new HashMap<Class<?>, MappingResourceMetadata>(); private final Map<PersistentProperty<?>, ResourceMapping> propertyCache = new HashMap<PersistentProperty<?>, ResourceMapping>(); /** * Creates a new {@link PersistentEntitiesResourceMappings} from the given {@link PersistentEntities}. * * @param entities must not be {@literal null}. */ public PersistentEntitiesResourceMappings(PersistentEntities entities) { this.entities = entities; } /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMappings#getMappingFor(java.lang.Class) */ @Override public ResourceMetadata getMetadataFor(Class<?> type) { Assert.notNull(type, "Type must not be null!"); type = ClassUtils.getUserClass(type); if (cache.containsKey(type)) { return cache.get(type); } MappingResourceMetadata metadata = getMappingMetadataFor(type); cache.put(type, metadata); return metadata; } /** * Returns the {@link MappingResourceMetadata} for the given type. * * @param type must not be {@literal null}. * @return the {@link MappingResourceMetadata} if the given type is a {@link PersistentEntity}, {@literal null} * otherwise. */ MappingResourceMetadata getMappingMetadataFor(Class<?> type) { Assert.notNull(type, "Type must not be null!"); Class<?> userType = ClassUtils.getUserClass(type); MappingResourceMetadata mappingMetadata = mappingCache.get(userType); if (mappingMetadata != null) { return mappingMetadata; } Optional<PersistentEntity<?, ? extends PersistentProperty<?>>> entity = entities.getPersistentEntity(userType); return entity.map(it -> { MappingResourceMetadata metadata = new MappingResourceMetadata(it, this); mappingCache.put(userType, metadata); return metadata; }).orElse(null); } /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMappings#getSearchResourceMappings(java.lang.Class) */ @Override public SearchResourceMappings getSearchResourceMappings(Class<?> domainType) { return searchResourceMappings; } /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMappings#exportsMappingFor(java.lang.Class) */ @Override public boolean exportsMappingFor(Class<?> type) { if (!hasMappingFor(type)) { return false; } ResourceMetadata metadata = getMetadataFor(type); return metadata.isExported(); } /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMappings#exportsTopLevelResourceFor(java.lang.String) */ @Override public boolean exportsTopLevelResourceFor(String path) { Assert.hasText(path, "Path must not be null or empty!"); for (ResourceMetadata metadata : this) { if (metadata.getPath().matches(path)) { return metadata.isExported(); } } return false; } /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMappings#hasMappingFor(java.lang.Class) */ @Override public boolean hasMappingFor(Class<?> type) { if (cache.containsKey(type)) { return true; } return false; } /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMetadataProvider#getMappingFor(org.springframework.data.mapping.PersistentProperty) */ public ResourceMapping getMappingFor(PersistentProperty<?> property) { ResourceMapping propertyMapping = propertyCache.get(property); if (propertyMapping != null) { return propertyMapping; } propertyMapping = new PersistentPropertyResourceMapping(property, this); propertyCache.put(property, propertyMapping); return propertyMapping; } public boolean isMapped(PersistentProperty<?> property) { ResourceMapping metadata = getMappingFor(property); return metadata != null && metadata.isExported(); } /* * (non-Javadoc) * @see java.lang.Iterable#iterator() */ @Override public Iterator<ResourceMetadata> iterator() { Set<ResourceMetadata> metadata = new HashSet<ResourceMetadata>(); for (ResourceMetadata candidate : cache.values()) { if (candidate != null) { metadata.add(candidate); } } return metadata.iterator(); } /** * Adds the given {@link ResourceMetadata} to the cache. * * @param type must not be {@literal null}. * @param metadata can be {@literal null}. */ protected final void addToCache(Class<?> type, ResourceMetadata metadata) { cache.put(type, metadata); } /** * Returns whether we currently already have {@link ResourceMetadata} for the given type. * * @param type must not be {@literal null}. * @return */ protected final boolean hasMetadataFor(Class<?> type) { return cache.containsKey(type); } }