/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.sling.resourceresolver.impl.helper; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceDecorator; import org.apache.sling.commons.osgi.ServiceUtil; /** * Helper class to track the resource decorators and keep them sorted by their * service ranking. */ public class ResourceDecoratorTracker { private static final ResourceDecorator[] EMPTY_ARRAY = new ResourceDecorator[0]; /** * The (optional) resource decorators, working copy. */ protected final List<ResourceDecoratorEntry> resourceDecorators = new ArrayList<ResourceDecoratorEntry>(); /** * An array of the above, updates when changes are created. */ private volatile ResourceDecorator[] resourceDecoratorsArray = EMPTY_ARRAY; public void close() { synchronized (this.resourceDecorators) { this.resourceDecorators.clear(); this.resourceDecoratorsArray = EMPTY_ARRAY; } } /** * Decorate a resource. */ public Resource decorate(final Resource resource) { Resource result = resource; final ResourceDecorator[] decorators = this.resourceDecoratorsArray; for (final ResourceDecorator decorator : decorators) { final Resource original = result; result = decorator.decorate(original); if (result == null) { result = original; } } // make resource metadata read-only result.getResourceMetadata().lock(); return result; } /** * Bind a resource decorator. */ public void bindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) { synchronized (this.resourceDecorators) { this.resourceDecorators.add(new ResourceDecoratorEntry(decorator, ServiceUtil.getComparableForServiceRanking(props))); Collections.sort(this.resourceDecorators); updateResourceDecoratorsArray(); } } /** * Unbind a resouce decorator. */ public void unbindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) { synchronized (this.resourceDecorators) { final Iterator<ResourceDecoratorEntry> i = this.resourceDecorators .iterator(); while (i.hasNext()) { final ResourceDecoratorEntry current = i.next(); if (current.decorator == decorator) { i.remove(); break; } } updateResourceDecoratorsArray(); } } /** * Updates the ResourceDecorators array, this method is not thread safe and * should only be called from a synchronized block. */ private void updateResourceDecoratorsArray() { final ResourceDecorator[] decorators; if (this.resourceDecorators.size() > 0) { decorators = new ResourceDecorator[this.resourceDecorators.size()]; int index = 0; final Iterator<ResourceDecoratorEntry> i = this.resourceDecorators .iterator(); while (i.hasNext()) { decorators[index] = i.next().decorator; index++; } } else { decorators = EMPTY_ARRAY; } this.resourceDecoratorsArray = decorators; } /** * Internal class to keep track of the resource decorators. */ private static final class ResourceDecoratorEntry implements Comparable<ResourceDecoratorEntry> { final Comparable<Object> comparable; final ResourceDecorator decorator; public ResourceDecoratorEntry(final ResourceDecorator d, final Comparable<Object> comparable) { this.comparable = comparable; this.decorator = d; } public int compareTo(final ResourceDecoratorEntry o) { return comparable.compareTo(o.comparable); } } }