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.extensions.breadcrumb;
018
019import java.util.ArrayList;
020import java.util.List;
021
022/**
023 * Default breadcrumb model implementation that should be good for 99% of the use cases out there.
024 * 
025 * @author eelcohillenius
026 */
027public class DefaultBreadCrumbsModel implements IBreadCrumbModel
028{
029        private static final long serialVersionUID = 1L;
030
031        /** The currently active participant, if any (possibly null). */
032        private IBreadCrumbParticipant activeParticipant = null;
033
034        /** Holds the current list of crumbs. */
035        private final List<IBreadCrumbParticipant> crumbs = new ArrayList<>();
036
037        /** listeners utility. */
038        private final BreadCrumbModelListenerSupport listenerSupport = new BreadCrumbModelListenerSupport();
039
040        /**
041         * Construct.
042         */
043        public DefaultBreadCrumbsModel()
044        {
045        }
046
047        /**
048         * @see org.apache.wicket.extensions.breadcrumb.IBreadCrumbModel#addListener(org.apache.wicket.extensions.breadcrumb.IBreadCrumbModelListener)
049         */
050        @Override
051        public final void addListener(final IBreadCrumbModelListener listener)
052        {
053                listenerSupport.addListener(listener);
054        }
055
056        /**
057         * @see org.apache.wicket.extensions.breadcrumb.IBreadCrumbModel#allBreadCrumbParticipants()
058         */
059        @Override
060        public final List<IBreadCrumbParticipant> allBreadCrumbParticipants()
061        {
062                return crumbs;
063        }
064
065        /**
066         * @see org.apache.wicket.extensions.breadcrumb.IBreadCrumbModel#getActive()
067         */
068        @Override
069        public IBreadCrumbParticipant getActive()
070        {
071                return activeParticipant;
072        }
073
074        /**
075         * @see org.apache.wicket.extensions.breadcrumb.IBreadCrumbModel#removeListener(org.apache.wicket.extensions.breadcrumb.IBreadCrumbModelListener)
076         */
077        @Override
078        public final void removeListener(final IBreadCrumbModelListener listener)
079        {
080                listenerSupport.removeListener(listener);
081        }
082
083        /**
084         * @see org.apache.wicket.extensions.breadcrumb.IBreadCrumbModel#setActive(org.apache.wicket.extensions.breadcrumb.IBreadCrumbParticipant)
085         */
086        @Override
087        public final void setActive(final IBreadCrumbParticipant breadCrumbParticipant)
088        {
089                // see if the bread crumb was already added, and if so,
090                // clean up the stack after (on top of) this bred crumb
091                // and notify listeners of the removal
092                int len = crumbs.size() - 1;
093                int i = len;
094                while (i > -1)
095                {
096                        IBreadCrumbParticipant temp = crumbs.get(i);
097
098                        // if we found the bread crumb
099                        if (breadCrumbParticipant.equals(temp))
100                        {
101                                // remove the bread crumbs after this one
102                                int j = len;
103                                while (j > i)
104                                {
105                                        // remove and fire event
106                                        IBreadCrumbParticipant removed = crumbs.remove(j--);
107                                        listenerSupport.fireBreadCrumbRemoved(removed);
108                                }
109
110                                // activate the bread crumb participant
111                                activate(breadCrumbParticipant);
112
113                                // we're done; the provided bread crumb is on top
114                                // and the content is replaced, so just return this function
115                                return;
116                        }
117
118                        i--;
119                }
120
121                // arriving here means we weren't able to find the bread crumb
122                // add the new crumb
123                crumbs.add(breadCrumbParticipant);
124
125                // and notify listeners
126                listenerSupport.fireBreadCrumbAdded(breadCrumbParticipant);
127
128                // activate the bread crumb participant
129                activate(breadCrumbParticipant);
130        }
131
132        /**
133         * Activates the bread crumb participant.
134         * 
135         * @param breadCrumbParticipant
136         *            The participant to activate
137         */
138        protected final void activate(final IBreadCrumbParticipant breadCrumbParticipant)
139        {
140                // get old value
141                IBreadCrumbParticipant previousParticipant = activeParticipant;
142
143                // and set the provided participant as the active one
144                activeParticipant = breadCrumbParticipant;
145
146                // fire bread crumb activated event
147                listenerSupport.fireBreadCrumbActivated(previousParticipant, breadCrumbParticipant);
148
149                // signal the bread crumb participant that it is selected as the
150                // currently active one
151                breadCrumbParticipant.onActivate(previousParticipant);
152        }
153}