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.head;
018
019import java.io.Serializable;
020import java.util.Comparator;
021
022import org.apache.wicket.Page;
023import org.apache.wicket.markup.head.ResourceAggregator.RecordedHeaderItem;
024import org.apache.wicket.markup.head.ResourceAggregator.RecordedHeaderItemLocation;
025
026/**
027 * Implements the default sorting algorithm for {@link HeaderItem}s. {@link PriorityHeaderItem}s are
028 * moved to the front, inverting the component order to convert the child-first into a parent-first
029 * order. If {@code renderPageFirst} is true, the head from the markup of a page is moved to the
030 * front of the header, directly after the priority header items.
031 * 
032 * @author papegaaij
033 */
034public class PriorityFirstComparator implements Comparator<RecordedHeaderItem>, Serializable
035{
036        protected static enum HeaderItemType
037        {
038                PRIORITY, PAGE, COMPONENT;
039        }
040
041        private final boolean renderPageFirst;
042
043        /**
044         * Construct.
045         * 
046         * @param renderPageFirst
047         *            when true, the header of the page is moved to the front.
048         */
049        public PriorityFirstComparator(boolean renderPageFirst)
050        {
051                this.renderPageFirst = renderPageFirst;
052        }
053
054        @Override
055        public int compare(RecordedHeaderItem o1, RecordedHeaderItem o2)
056        {
057                HeaderItemType o1Type = getItemType(o1);
058                HeaderItemType o2Type = getItemType(o2);
059
060                if (o1Type != o2Type)
061                {
062                        return o1Type.ordinal() - o2Type.ordinal();
063                }
064
065                if (o1Type == HeaderItemType.PRIORITY)
066                {
067                        return inversedComponentOrder(o1, o2);
068                }
069                return compareWithinGroup(o1, o2);
070        }
071
072        /**
073         * Compares two header items that belong in the same group.
074         * 
075         * @param item1
076         * @param item2
077         * @return 0 by default to preserve the order
078         */
079        protected int compareWithinGroup(RecordedHeaderItem item1, RecordedHeaderItem item2)
080        {
081                return 0;
082        }
083
084        /**
085         * Compare two recorded {@link PriorityHeaderItem}s, converting the child-first order into
086         * parent-first.
087         * 
088         * @param item1
089         *            first item
090         * @param item2
091         *            second item
092         * @return -1, 0 or 1 if item1 needs to be rendered before, unchanged or after item2.
093         * 
094         * @see RecordedHeaderItemLocation#getDepth()
095         */
096        protected int inversedComponentOrder(RecordedHeaderItem item1, RecordedHeaderItem item2)
097        {
098                return item1.getMinDepth() - item2.getMinDepth();
099        }
100
101        /**
102         * Determines the type of the item: priority, page or component.
103         * 
104         * @param item
105         * @return the type of the item
106         */
107        protected HeaderItemType getItemType(RecordedHeaderItem item)
108        {
109                if (item.getItem() instanceof PriorityHeaderItem)
110                {
111                        return HeaderItemType.PRIORITY;
112                }
113
114                if (renderPageFirst)
115                {
116                        if (item.getItem() instanceof PageHeaderItem)
117                        {
118                                return HeaderItemType.PAGE;
119                        }
120
121                        for (RecordedHeaderItemLocation curLocation : item.getLocations())
122                        {
123                                if (curLocation.getRenderBase() instanceof Page)
124                                {
125                                        return HeaderItemType.PAGE;
126                                }
127                        }
128                }
129
130                return HeaderItemType.COMPONENT;
131        }
132}