001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html;
018
019import java.util.List;
020import java.util.concurrent.CopyOnWriteArrayList;
021
022import org.apache.wicket.markup.head.IHeaderResponse;
023import org.apache.wicket.markup.head.ResourceAggregator;
024import org.apache.wicket.util.lang.Args;
025
026/**
027 * A collection of {@link IHeaderResponseDecorator}s. The decorators will be nested oldest on the
028 * inside, newest on the outside. By default {@link ResourceAggregator} is already registered.
029 * 
030 * @author Emond Papegaaij
031 */
032public class HeaderResponseDecoratorCollection implements IHeaderResponseDecorator
033{
034        private final List<IHeaderResponseDecorator> decorators = new CopyOnWriteArrayList<>();
035
036        private IHeaderResponseDecorator resourceAggregation = ResourceAggregator::new;
037
038        public HeaderResponseDecoratorCollection()
039        {
040                decorators.add(resourceAggregation);
041        }
042
043        /**
044         * Adds a new {@link IHeaderResponseDecorator} that will decorates prior to all already
045         * registered decorators. That means, the first to be added will be wrapped by a
046         * {@link ResourceAggregator} like this: {@code new ResourceAggregator(first)}. The second will
047         * be wrapped by the first and the aggregator: {@code new ResourceAggregator(first(second))}.
048         * 
049         * @param decorator
050         *            The decorator to add, cannot be null.
051         * @return {@code this} for chaining.
052         */
053        public HeaderResponseDecoratorCollection add(IHeaderResponseDecorator decorator)
054        {
055                Args.notNull(decorator, "decorator");
056                decorators.add(0, decorator);
057                return this;
058        }
059
060        /**
061         * Adds a new {@link IHeaderResponseDecorator} that decorates immediately prior to resource
062         * aggregation.
063         * 
064         * @param decorator
065         *            The decorator to add, cannot be null.
066         * @return {@code this} for chaining.
067         * 
068         * @see ResourceAggregator
069         */
070        public HeaderResponseDecoratorCollection addPreResourceAggregationDecorator(
071                IHeaderResponseDecorator decorator)
072        {
073                Args.notNull(decorator, "decorator");
074
075                for (int i = 0; i < decorators.size(); i++)
076                {
077                        if (decorators.get(i) == resourceAggregation)
078                        {
079                                decorators.add(i, decorator);
080                                return this;
081                        }
082                }
083
084                throw new IllegalStateException("no resource aggregation");
085        }
086
087        /**
088         * Adds a new {@link IHeaderResponseDecorator} that decorates after all already registered
089         * decorators.
090         * 
091         * @param decorator
092         *            The decorator to add, cannot be null.
093         * @return {@code this} for chaining.
094         */
095        public HeaderResponseDecoratorCollection addPostProcessingDecorator(
096                IHeaderResponseDecorator decorator)
097        {
098                Args.notNull(decorator, "decorator");
099                decorators.add(decorator);
100                return this;
101        }
102
103        /**
104         * Replaces all registered {@link IHeaderResponseDecorator}s with the given decorator. This also
105         * removes the {@link ResourceAggregator}, which is required to render resource dependencies.
106         * 
107         * @param decorator
108         *            The decorator to add, cannot be null.
109         * @return {@code this} for chaining.
110         */
111        public HeaderResponseDecoratorCollection replaceAll(IHeaderResponseDecorator decorator)
112        {
113                Args.notNull(decorator, "decorator");
114                decorators.clear();
115                resourceAggregation = null;
116                decorators.add(decorator);
117                return this;
118        }
119
120        @Override
121        public IHeaderResponse decorate(IHeaderResponse response)
122        {
123                IHeaderResponse ret = response;
124                for (IHeaderResponseDecorator curDecorator : decorators)
125                {
126                        ret = curDecorator.decorate(ret);
127                }
128                return ret;
129        }
130}