/* Copyright 2013 the original author or authors. <p/> 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 <p/> http://www.apache.org/licenses/LICENSE-2.0 <p/> 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 io.neba.core.resourcemodels.adaptation; import io.neba.core.resourcemodels.caching.ResourceModelCaches; import io.neba.core.resourcemodels.mapping.ResourceToModelMapper; import io.neba.core.resourcemodels.registration.LookupResult; import io.neba.core.resourcemodels.registration.ModelRegistry; import io.neba.core.util.Key; import io.neba.core.util.OsgiBeanSource; import org.apache.sling.api.adapter.AdapterFactory; import org.apache.sling.api.resource.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Collection; import static org.apache.commons.lang.StringUtils.join; /** * Adapts a {@link Resource} to it's {@link io.neba.api.annotations.ResourceModel} using the * {@link ModelRegistry} to lookup and the {@link ResourceToModelMapper} to map the model. * * @author Olaf Otto * @see ResourceToModelAdapterUpdater */ @Service public class ResourceToModelAdapter implements AdapterFactory { @Autowired private ModelRegistry registry; @Autowired private ResourceToModelMapper mapper; @Autowired private ResourceModelCaches caches; /** * @return the resource model provided by the * {@link io.neba.core.resourcemodels.registration.ModelRegistrar} * or <code>null</code> if no model for the given resource exists or * the resource model's type does not match the target type. */ @Override public <T> T getAdapter(Object adaptable, Class<T> target) { T model = null; if (adaptable instanceof Resource) { Resource resource = (Resource) adaptable; Key key = new Key(resource.getPath(), target, resource.getResourceType(), resource.getResourceResolver().hashCode()); model = this.caches.lookup(key); if (model == null) { model = getAdapterInternal(target, resource); if (model != null) { this.caches.store(resource, model, key); } } } return model; } @SuppressWarnings("unchecked") private <T> T getAdapterInternal(Class<T> target, Resource resource) { T model = null; Collection<LookupResult> models = this.registry.lookupMostSpecificModels(resource, target); if (models != null && !models.isEmpty()) { if (models.size() == 1) { OsgiBeanSource<?> source = models.iterator().next().getSource(); model = (T) this.mapper.map(resource, source); } else { throw new AmbiguousModelAssociationException("There is more than one model that maps " + resource.getPath() + " to " + target.getName() + ": " + join(models, ", ") + "."); } } return model; } }