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;
018
019import java.io.Serializable;
020import java.util.ArrayDeque;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.Deque;
025import java.util.Iterator;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Locale;
029import java.util.Map;
030import java.util.Spliterators;
031import java.util.stream.Stream;
032import java.util.stream.StreamSupport;
033
034import org.apache.commons.collections4.map.LinkedMap;
035import org.apache.wicket.core.util.string.ComponentStrings;
036import org.apache.wicket.markup.ComponentTag;
037import org.apache.wicket.markup.ComponentTag.IAutoComponentFactory;
038import org.apache.wicket.markup.IMarkupFragment;
039import org.apache.wicket.markup.Markup;
040import org.apache.wicket.markup.MarkupElement;
041import org.apache.wicket.markup.MarkupException;
042import org.apache.wicket.markup.MarkupFactory;
043import org.apache.wicket.markup.MarkupNotFoundException;
044import org.apache.wicket.markup.MarkupStream;
045import org.apache.wicket.markup.MarkupType;
046import org.apache.wicket.markup.WicketTag;
047import org.apache.wicket.markup.html.border.Border;
048import org.apache.wicket.markup.html.form.AutoLabelResolver;
049import org.apache.wicket.markup.resolver.ComponentResolvers;
050import org.apache.wicket.model.IComponentInheritedModel;
051import org.apache.wicket.model.IModel;
052import org.apache.wicket.model.IWrapModel;
053import org.apache.wicket.settings.DebugSettings;
054import org.apache.wicket.util.lang.Args;
055import org.apache.wicket.util.lang.Classes;
056import org.apache.wicket.util.lang.Generics;
057import org.apache.wicket.util.string.Strings;
058import org.apache.wicket.util.visit.ClassVisitFilter;
059import org.apache.wicket.util.visit.IVisit;
060import org.apache.wicket.util.visit.IVisitor;
061import org.apache.wicket.util.visit.Visits;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065/**
066 * A MarkupContainer holds a map of child components.
067 * <ul>
068 * <li><b>Children </b>- Children can be added by calling the {@link #add(Component...)} method, and
069 * they can be looked up using a colon separated path. For example, if a container called "a" held a
070 * nested container "b" which held a nested component "c", then a.get("b:c") would return the
071 * Component with id "c". The number of children in a MarkupContainer can be determined by calling
072 * size(), and the whole hierarchy of children held by a MarkupContainer can be traversed by calling
073 * visitChildren(), passing in an implementation of IVisitor.</li>
074 * 
075 * <li><b>Markup Rendering </b>- A MarkupContainer also holds/references associated markup which is
076 * used to render the container. As the markup stream for a container is rendered, component
077 * references in the markup are resolved by using the container to look up Components in the
078 * container's component map by id. Each component referenced by the markup stream is given an
079 * opportunity to render itself using the markup stream.</li>
080 * </ul>
081 * <p>
082 * Components may alter their referring tag, replace the tag's body or insert markup after the tag.
083 * But components cannot remove tags from the markup stream. This is an important guarantee because
084 * graphic designers may be setting attributes on component tags that affect visual presentation.
085 * <p>
086 * The type of markup held in a given container subclass can be determined by calling
087 * {@link #getMarkupType()}. Markup is accessed via a MarkupStream object which allows a component
088 * to traverse ComponentTag and RawMarkup MarkupElements while rendering a response. Markup in the
089 * stream may be HTML or some other kind of markup, such as VXML, as determined by the specific
090 * container subclass.
091 * <p>
092 * A markup stream may be directly associated with a container via setMarkupStream. However, a
093 * container which does not have a markup stream (its getMarkupStream() returns null) may inherit a
094 * markup stream from a container above it in the component hierarchy. The
095 * {@link #findMarkupStream()} method will locate the first container at or above this container
096 * which has a markup stream.
097 * <p>
098 * All Page containers set a markup stream before rendering by calling the method
099 * {@link #getAssociatedMarkupStream(boolean)} to load the markup associated with the page. Since
100 * Page is at the top of the container hierarchy, it is guaranteed that {@link #findMarkupStream()}
101 * will always return a valid markup stream.
102 * 
103 * @see MarkupStream
104 * @author Jonathan Locke
105 */
106public abstract class MarkupContainer extends Component implements Iterable<Component>
107{
108        private static final long serialVersionUID = 1L;
109
110        private static final int INITIAL_CHILD_LIST_CAPACITY = 12;
111
112        /**
113         * The threshold where we start using a Map to store children in, replacing a List. Adding
114         * components to a list is O(n), and to a map O(1). The magic number is 24, due to a Map using
115         * more memory to store its elements and below 24 children there's no discernible difference
116         * between adding to a Map or a List.
117         * 
118         * We have focused on adding elements to a list, instead of indexed lookups because adding is an
119         * action that is performed very often, and lookups often are done by component IDs, not index.
120         */
121        static final int MAPIFY_THRESHOLD = 24; // 32 * 0.75
122
123        /** Log for reporting. */
124        private static final Logger log = LoggerFactory.getLogger(MarkupContainer.class);
125
126        /**
127         * Metadata key for looking up the list of removed children necessary for tracking modifications
128         * during iteration of the children of this markup container.
129         * 
130         * This is stored in meta data because it only is necessary when a child is removed, and this
131         * saves the memory necessary for a field on a widely used class.
132         */
133        private static final MetaDataKey<LinkedList<RemovedChild>> REMOVALS_KEY = new MetaDataKey<>()
134        {
135                private static final long serialVersionUID = 1L;
136        };
137
138        /**
139         * Administrative class for detecting removed children during child iteration. Not intended to
140         * be serializable but for e.g. determining the size of the component it has to be serializable.
141         */
142        private static class RemovedChild implements Serializable
143        {
144                private static final long serialVersionUID = 1L;
145
146                private transient final Component removedChild;
147                private transient final Component previousSibling;
148
149                private RemovedChild(Component removedChild, Component previousSibling)
150                {
151                        this.removedChild = removedChild;
152                        this.previousSibling = previousSibling;
153                }
154        }
155
156        /**
157         * Administrative counter to keep track of modifications to the list of children during
158         * iteration.
159         * 
160         * When the {@link #children_size()} changes due to an addition or removal of a child component,
161         * the modCounter is increased. This way iterators that iterate over the children of this
162         * container can keep track when they need to change their iteration strategy.
163         */
164        private transient int modCounter = 0;
165
166        /**
167         * The children of this markup container, if any. Can be a Component when there's only one
168         * child, a List when the number of children is fewer than {@link #MAPIFY_THRESHOLD} or a Map
169         * when there are more children.
170         */
171        private Object children;
172
173        public MarkupContainer(final String id)
174        {
175                this(id, null);
176        }
177
178        public MarkupContainer(final String id, IModel<?> model)
179        {
180                super(id, model);
181        }
182
183        /**
184         * Adds the child component(s) to this container.
185         * 
186         * @param children
187         *            The child(ren) to add.
188         * @throws IllegalArgumentException
189         *             Thrown if a child with the same id is replaced by the add operation.
190         * @return This
191         */
192        public MarkupContainer add(final Component... children)
193        {
194                for (Component child : children)
195                {
196                        Args.notNull(child, "child");
197
198                        if (this == child)
199                        {
200                                throw new IllegalArgumentException(
201                                        exceptionMessage("Trying to add this component to itself."));
202                        }
203
204                        MarkupContainer parent = getParent();
205                        while (parent != null)
206                        {
207                                if (child == parent)
208                                {
209                                        String msg = "You can not add a component's parent as child to the component (loop): Component: " +
210                                                this.toString(false) + "; parent == child: " + parent.toString(false);
211
212                                        if (child instanceof Border.BorderBodyContainer)
213                                        {
214                                                msg += ". Please consider using Border.addToBorder(new " +
215                                                        Classes.simpleName(this.getClass()) + "(\"" + this.getId() +
216                                                        "\", ...) instead of add(...)";
217                                        }
218
219                                        throw new WicketRuntimeException(msg);
220                                }
221
222                                parent = parent.getParent();
223                        }
224
225                        checkHierarchyChange(child);
226
227                        if (log.isDebugEnabled())
228                        {
229                                log.debug("Add " + child.getId() + " to " + this);
230                        }
231
232                        // Add the child to my children
233                        Component previousChild = children_put(child);
234                        if (previousChild != null && previousChild != child)
235                        {
236                                throw new IllegalArgumentException(
237                                        exceptionMessage("A child '" + previousChild.getClass().getSimpleName() +
238                                                "' with id '" + child.getId() + "' already exists"));
239                        }
240
241                        addedComponent(child);
242
243                }
244                return this;
245        }
246
247        /**
248         * Replaces a child component of this container with another or just adds it in case no child
249         * with the same id existed yet.
250         * 
251         * @param children
252         *            The child(ren) to be added or replaced
253         * @return this markup container
254         */
255        public MarkupContainer addOrReplace(final Component... children)
256        {
257                for (Component child : children)
258                {
259                        Args.notNull(child, "child");
260
261                        checkHierarchyChange(child);
262
263                        if (get(child.getId()) == null)
264                        {
265                                add(child);
266                        }
267                        else
268                        {
269                                replace(child);
270                        }
271                }
272
273                return this;
274        }
275
276        /**
277         * This method allows a component to be added by an auto-resolver such as AutoLinkResolver.
278         * While the component is being added, the component's FLAG_AUTO boolean is set. The isAuto()
279         * method of Component returns true if a component or any of its parents has this bit set. When
280         * a component is added via autoAdd(), the logic in Page that normally (a) checks for
281         * modifications during the rendering process, and (b) versions components, is bypassed if
282         * Component.isAuto() returns true.
283         * <p>
284         * The result of all this is that components added with autoAdd() are free from versioning and
285         * can add their own children without the usual exception that would normally be thrown when the
286         * component hierarchy is modified during rendering.
287         * 
288         * @param component
289         *            The component to add
290         * @param markupStream
291         *            Null, if the parent container is able to provide the markup. Else the markup
292         *            stream to be used to render the component.
293         * @return True, if component has been added
294         */
295        public final boolean autoAdd(final Component component, MarkupStream markupStream)
296        {
297                Args.notNull(component, "component");
298
299                // Replace strategy
300                component.setAuto(true);
301
302                if (markupStream != null)
303                {
304                        component.setMarkup(markupStream.getMarkupFragment());
305                }
306
307                // Add the child to the parent.
308
309                // Arguably child.setParent() can be used as well. It connects the child to the parent and
310                // that's all what most auto-components need. Unfortunately child.onDetach() will not / can
311                // not be invoked, since the parent doesn't known its one of his children. Hence we need to
312                // properly add it.
313                children_remove(component.getId());
314                add(component);
315
316                return true;
317        }
318
319        /**
320         * @param component
321         *            The component to check
322         * @param recurse
323         *            True if all descendents should be considered
324         * @return True if the component is contained in this container
325         */
326        public boolean contains(final Component component, final boolean recurse)
327        {
328                Args.notNull(component, "component");
329
330                if (recurse)
331                {
332                        // Start at component and continue while we're not out of parents
333                        for (Component current = component; current != null;)
334                        {
335                                // Get parent
336                                final MarkupContainer parent = current.getParent();
337
338                                // If this container is the parent, then the component is
339                                // recursively contained by this container
340                                if (parent == this)
341                                {
342                                        // Found it!
343                                        return true;
344                                }
345
346                                // Move up the chain to the next parent
347                                current = parent;
348                        }
349
350                        // Failed to find this container in component's ancestry
351                        return false;
352                }
353                else
354                {
355                        // Is the component contained in this container?
356                        return component.getParent() == this;
357                }
358        }
359
360        /**
361         * Get a child component by looking it up with the given path.
362         * <p>
363         * A component path consists of component ids separated by colons, e.g. "b:c" identifies a
364         * component "c" inside container "b" inside this container.
365         * 
366         * @param path
367         *            path to component
368         * @return The component at the path
369         */
370        @Override
371        public final Component get(String path)
372        {
373                // Reference to this container
374                if (Strings.isEmpty(path))
375                {
376                        return this;
377                }
378
379                // process parent .. references
380
381                MarkupContainer container = this;
382
383                String id = Strings.firstPathComponent(path, Component.PATH_SEPARATOR);
384
385                while (Component.PARENT_PATH.equals(id))
386                {
387                        container = container.getParent();
388                        if (container == null)
389                        {
390                                return null;
391                        }
392                        path = path.length() == id.length() ? "" : path.substring(id.length() + 1);
393                        id = Strings.firstPathComponent(path, Component.PATH_SEPARATOR);
394                }
395
396                if (Strings.isEmpty(id))
397                {
398                        return container;
399                }
400
401                // Get child by id
402                Component child = container.children_get(id);
403
404                // Found child?
405                if (child != null)
406                {
407                        String path2 = Strings.afterFirstPathComponent(path, Component.PATH_SEPARATOR);
408
409                        // Recurse on latter part of path
410                        return child.get(path2);
411                }
412
413                return null;
414        }
415
416        /**
417         * Gets a fresh markup stream that contains the (immutable) markup resource for this class.
418         * 
419         * @param throwException
420         *            If true, throw an exception, if markup could not be found
421         * @return A stream of MarkupElement elements
422         */
423        public MarkupStream getAssociatedMarkupStream(final boolean throwException)
424        {
425                IMarkupFragment markup = getAssociatedMarkup();
426
427                // If we found markup for this container
428                if (markup != null)
429                {
430                        return new MarkupStream(markup);
431                }
432
433                if (throwException == true)
434                {
435                        // throw exception since there is no associated markup
436                        throw new MarkupNotFoundException(
437                                "Markup of type '" +
438                                        getMarkupType().getExtension() +
439                                        "' for component '" +
440                                        getClass().getName() +
441                                        "' not found." +
442                                        " Enable debug messages for org.apache.wicket.util.resource to get a list of all filenames tried.: " +
443                                        toString());
444                }
445
446                return null;
447        }
448
449        /**
450         * Gets a fresh markup stream that contains the (immutable) markup resource for this class.
451         * 
452         * @return A stream of MarkupElement elements. Null if not found.
453         */
454        public Markup getAssociatedMarkup()
455        {
456                try
457                {
458                        Markup markup = MarkupFactory.get().getMarkup(this, false);
459
460                        // If we found markup for this container
461                        if ((markup != null) && (markup != Markup.NO_MARKUP))
462                        {
463                                return markup;
464                        }
465
466                        return null;
467                }
468                catch (MarkupException ex)
469                {
470                        // re-throw it. The exception contains already all the information
471                        // required.
472                        throw ex;
473                }
474                catch (MarkupNotFoundException ex)
475                {
476                        // re-throw it. The exception contains already all the information
477                        // required.
478                        throw ex;
479                }
480                catch (WicketRuntimeException ex)
481                {
482                        // throw exception since there is no associated markup
483                        throw new MarkupNotFoundException(
484                                exceptionMessage("Markup of type '" + getMarkupType().getExtension() +
485                                        "' for component '" + getClass().getName() + "' not found." +
486                                        " Enable debug messages for org.apache.wicket.util.resource to get a list of all filenames tried"),
487                                ex);
488                }
489        }
490
491        /**
492         * Get the markup of the child.
493         * 
494         * @see Component#getMarkup()
495         * 
496         * @param child
497         *            The child component. If null, the container's markup will be returned. See Border,
498         *            Panel or Enclosure where getMarkup(null) != getMarkup().
499         * @return The child's markup
500         */
501        public IMarkupFragment getMarkup(final Component child)
502        {
503                // Delegate request to attached markup sourcing strategy.
504                return getMarkupSourcingStrategy().getMarkup(this, child);
505        }
506
507        /**
508         * Get the type of associated markup for this component. The markup type for a component is
509         * independent of whether or not the component actually has an associated markup resource file
510         * (which is determined at runtime).
511         * 
512         * @return The type of associated markup for this component (for example, "html", "wml" or
513         *         "vxml"). If there is no markup type for a component, null may be returned, but this
514         *         means that no markup can be loaded for the class. Null is also returned if the
515         *         component, or any of its parents, has not been added to a Page.
516         */
517        public MarkupType getMarkupType()
518        {
519                MarkupContainer parent = getParent();
520                if (parent != null)
521                {
522                        return parent.getMarkupType();
523                }
524                return null;
525        }
526
527        /**
528         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT.
529         * 
530         * Adds a child component to this container.
531         * 
532         * @param child
533         *            The child
534         * @throws IllegalArgumentException
535         *             Thrown if a child with the same id is replaced by the add operation.
536         */
537        public void internalAdd(final Component child)
538        {
539                if (log.isDebugEnabled())
540                {
541                        log.debug("internalAdd " + child.getId() + " to " + this);
542                }
543
544                // Add to map
545                children_put(child);
546                addedComponent(child);
547        }
548
549        /**
550         * Gives an iterator that allow you to iterate through the children of this markup container in
551         * the order the children were added. The iterator supports additions and removals from the list
552         * of children during iteration.
553         * 
554         * @return Iterator that iterates through children in the order they were added
555         */
556        @Override
557        public Iterator<Component> iterator()
558        {
559                /**
560                 * Iterator that knows how to change between a single child, list of children and map of
561                 * children. Keeps track when the iterator was last sync'd with the markup container's
562                 * tracking of changes to the list of children.
563                 */
564                class MarkupChildIterator implements Iterator<Component>
565                {
566                        private int indexInRemovalsSinceLastUpdate;
567                        private int expectedModCounter = -1;
568                        private Component currentComponent = null;
569                        private Iterator<Component> internalIterator = null;
570
571                        @Override
572                        public boolean hasNext()
573                        {
574                                refreshInternalIteratorIfNeeded();
575                                return internalIterator.hasNext();
576                        }
577
578                        @Override
579                        public Component next()
580                        {
581                                refreshInternalIteratorIfNeeded();
582                                return currentComponent = internalIterator.next();
583                        }
584
585                        @Override
586                        public void remove()
587                        {
588                                MarkupContainer.this.remove(currentComponent);
589                                refreshInternalIteratorIfNeeded();
590                        }
591
592                        private void refreshInternalIteratorIfNeeded()
593                        {
594                                if (expectedModCounter >= modCounter) {
595                                        // no new modifications
596                                        return;
597                                }
598
599                                if (children == null)
600                                {
601                                        internalIterator = Collections.emptyIterator();
602                                }
603                                else if (children instanceof Component)
604                                {
605                                        internalIterator = Collections.singleton((Component)children).iterator();
606                                }
607                                else if (children instanceof List)
608                                {
609                                        List<Component> childrenList = children();
610                                        internalIterator = childrenList.iterator();
611                                }
612                                else
613                                {
614                                        Map<String, Component> childrenMap = children();
615                                        internalIterator = childrenMap.values().iterator();
616                                }
617
618                                // since we now have a new iterator, we need to set it to the last known position
619                                currentComponent = findLastExistingChildAlreadyReturned(currentComponent);
620                                expectedModCounter = modCounter;
621
622                                if (currentComponent != null)
623                                {
624                                        // move the new internal iterator to the place of the last processed component
625                                        while (internalIterator.hasNext() &&
626                                                internalIterator.next() != currentComponent)
627                                                // noop
628                                                ;
629                                }
630                        }
631
632                        private Component findLastExistingChildAlreadyReturned(Component current)
633                        {
634                                if (current == null) {
635                                        indexInRemovalsSinceLastUpdate = 0;
636                                } else {
637                                        LinkedList<RemovedChild> removals = removals_get();
638                                        if (removals != null) {
639                                                check_removed:
640                                                while (current != null)
641                                                {
642                                                        for (int i = indexInRemovalsSinceLastUpdate; i < removals.size(); i++)
643                                                        {
644                                                                RemovedChild removal = removals.get(i);
645                                                                if (removal.removedChild == current ||
646                                                                        removal.removedChild == null)
647                                                                {
648                                                                        current = removal.previousSibling;
649                                                                        
650                                                                        // current was removed, use its sibling instead
651                                                                        continue check_removed;
652                                                                }
653                                                        }
654                                                        
655                                                        // current wasn't removed, keep it
656                                                        break;
657                                                }
658                                                
659                                                indexInRemovalsSinceLastUpdate = removals.size();
660                                        }
661                                }
662                                
663                                return current;
664                        }
665                };
666                return new MarkupChildIterator();
667        }
668
669        /**
670         * Creates an iterator that iterates over children in the order specified by comparator. This
671         * works on a copy of the children list.
672         * 
673         * @param comparator
674         *            The comparator
675         * @return Iterator that iterates over children in the order specified by comparator
676         */
677        public final Iterator<Component> iterator(Comparator<Component> comparator)
678        {
679                final List<Component> sorted = copyChildren();
680                Collections.sort(sorted, comparator);
681                return sorted.iterator();
682        }
683
684        /**
685         * Removes a component from the children identified by the {@code component.getId()}
686         * 
687         * @param component
688         *            Component to remove from this container
689         * @return {@code this} for chaining
690         */
691        public MarkupContainer remove(final Component component)
692        {
693                checkHierarchyChange(component);
694
695                Args.notNull(component, "component");
696
697                children_remove(component.getId());
698                removedComponent(component);
699
700                return this;
701        }
702
703        /**
704         * Removes the given component
705         * 
706         * @param id
707         *            The id of the component to remove
708         * @return {@code this} for chaining
709         */
710        public MarkupContainer remove(final String id)
711        {
712                Args.notNull(id, "id");
713
714                final Component component = get(id);
715                if (component != null)
716                {
717                        remove(component);
718                }
719                else
720                {
721                        throw new WicketRuntimeException("Unable to find a component with id '" + id +
722                                "' to remove");
723                }
724
725                return this;
726        }
727
728        /**
729         * Removes all children from this container.
730         * <p>
731         * Note: implementation does not call {@link MarkupContainer#remove(Component) } for each
732         * component.
733         * 
734         * @return {@code this} for method chaining
735         */
736        public MarkupContainer removeAll()
737        {
738                if (children != null)
739                {
740                        addStateChange();
741
742                        for (Component child : this)
743                        {
744                                // Do not call remove() because the state change would then be
745                                // recorded twice.
746                                child.internalOnRemove();
747                                child.detach();
748                                child.setParent(null);
749                        }
750
751                        children = null;
752                        removals_add(null, null);
753                }
754
755                return this;
756        }
757
758        /**
759         * Renders the entire associated markup for a container such as a Border or Panel. Any leading
760         * or trailing raw markup in the associated markup is skipped.
761         * 
762         * @param openTagName
763         *            the tag to render the associated markup for
764         * @param exceptionMessage
765         *            ignored
766         * @deprecated
767         *            Use {@link #renderAssociatedMarkup(String)}. The {@code exceptionMessage}
768         *            parameter is ignored.
769         */
770        @Deprecated(since = "9.10.0", forRemoval = true)
771        public final void renderAssociatedMarkup(final String openTagName, final String exceptionMessage)
772        {
773                renderAssociatedMarkup(openTagName);
774        }
775        
776        /**
777         * Renders the entire associated markup for a container such as a Border or Panel. Any leading
778         * or trailing raw markup in the associated markup is skipped.
779         * 
780         * @param openTagName
781         *            the tag to render the associated markup for
782         */
783        public final void renderAssociatedMarkup(final String openTagName)
784        {
785                // Get associated markup file for the Border or Panel component
786                final MarkupStream associatedMarkupStream = new MarkupStream(getMarkup(null));
787
788                // Get open tag in associated markup of border component
789                MarkupElement elem = associatedMarkupStream.get();
790                if ((elem instanceof ComponentTag) == false)
791                {
792                        associatedMarkupStream.throwMarkupException("Expected the open tag. Markup for a "
793                                + openTagName + " component must begin a tag like '<wicket:" + openTagName + ">'");
794                }
795
796                // Check for required open tag name
797                ComponentTag associatedMarkupOpenTag = (ComponentTag)elem;
798                if (!(associatedMarkupOpenTag.isOpen() && (associatedMarkupOpenTag instanceof WicketTag)))
799                {
800                        associatedMarkupStream.throwMarkupException("Markup for a " + openTagName
801                                + " component must begin a tag like '<wicket:" + openTagName + ">'");
802                }
803
804                try
805                {
806                        setIgnoreAttributeModifier(true);
807                        renderComponentTag(associatedMarkupOpenTag);
808                        associatedMarkupStream.next();
809
810                        String className = null;
811
812                        final boolean outputClassName = getApplication().getDebugSettings()
813                                .isOutputMarkupContainerClassName();
814                        if (outputClassName)
815                        {
816                                className = Classes.name(getClass());
817                                getResponse().write("<!-- MARKUP FOR ");
818                                getResponse().write(className);
819                                getResponse().write(" BEGIN -->");
820                        }
821
822                        renderComponentTagBody(associatedMarkupStream, associatedMarkupOpenTag);
823
824                        if (outputClassName)
825                        {
826                                getResponse().write("<!-- MARKUP FOR ");
827                                getResponse().write(className);
828                                getResponse().write(" END -->");
829                        }
830
831                        renderClosingComponentTag(associatedMarkupStream, associatedMarkupOpenTag, false);
832                }
833                finally
834                {
835                        setIgnoreAttributeModifier(false);
836                }
837        }
838
839        /**
840         * Replaces a child component of this container with another
841         * 
842         * @param child
843         *            The child
844         * @throws IllegalArgumentException
845         *             Thrown if there was no child with the same id.
846         * @return This
847         */
848        public MarkupContainer replace(final Component child)
849        {
850                Args.notNull(child, "child");
851
852                checkHierarchyChange(child);
853
854                if (log.isDebugEnabled())
855                {
856                        log.debug("Replacing " + child.getId() + " in " + this);
857                }
858
859                if (child.getParent() != this)
860                {
861                        final Component replaced = children_put(child);
862
863                        // Look up to make sure it was already in the map
864                        if (replaced == null)
865                        {
866                                throw new WicketRuntimeException(
867                                        exceptionMessage("Cannot replace a component which has not been added: id='" +
868                                                child.getId() + "', component=" + child));
869                        }
870
871                        // first remove the component.
872                        removedComponent(replaced);
873
874                        // The generated markup id remains the same
875                        child.setMarkupId(replaced);
876
877                        // then add the other one.
878                        addedComponent(child);
879                }
880
881                return this;
882        }
883
884        @Override
885        public MarkupContainer setDefaultModel(final IModel<?> model)
886        {
887                final IModel<?> previous = getModelImpl();
888                super.setDefaultModel(model);
889                if (previous instanceof IComponentInheritedModel)
890                {
891                        visitChildren(new IVisitor<Component, Void>()
892                        {
893                                @Override
894                                public void component(final Component component, final IVisit<Void> visit)
895                                {
896                                        IModel<?> compModel = component.getDefaultModel();
897                                        if (compModel instanceof IWrapModel)
898                                        {
899                                                compModel = ((IWrapModel<?>)compModel).getWrappedModel();
900                                        }
901                                        if (compModel == previous)
902                                        {
903                                                component.setDefaultModel(null);
904                                        }
905                                        else if (compModel == model)
906                                        {
907                                                component.modelChanged();
908                                        }
909                                }
910
911                        });
912                }
913                return this;
914        }
915
916        /**
917         * Get the number of children in this container.
918         * 
919         * @return Number of children in this container
920         */
921        public int size()
922        {
923                return children_size();
924        }
925
926        @Override
927        public String toString()
928        {
929                return toString(false);
930        }
931
932        /**
933         * @param detailed
934         *            True if a detailed string is desired
935         * @return String representation of this container
936         */
937        @Override
938        public String toString(final boolean detailed)
939        {
940                final StringBuilder buffer = new StringBuilder();
941                buffer.append('[').append(Classes.simpleName(this.getClass())).append(' ');
942                buffer.append(super.toString(detailed));
943                if (detailed && children_size() != 0)
944                {
945
946                        buffer.append(", children = ");
947
948                        // Loop through child components
949                        boolean first = true;
950                        for (Component child : this)
951                        {
952                                if (first)
953                                {
954                                        buffer.append(' ');
955                                        first = false;
956                                }
957                                buffer.append(child.toString());
958                        }
959
960                }
961                buffer.append(']');
962                return buffer.toString();
963        }
964
965        /**
966         * Traverses all child components of the given class in this container, calling the visitor's
967         * visit method at each one.
968         * 
969         * Make sure that if you give a type S that the clazz parameter will only resolve to those
970         * types. Else a class cast exception will occur.
971         * 
972         * @param <S>
973         *            The type that goes into the Visitor.component() method.
974         * @param <R>
975         * @param clazz
976         *            The class of child to visit
977         * @param visitor
978         *            The visitor to call back to
979         * @return The return value from a visitor which halted the traversal, or null if the entire
980         *         traversal occurred
981         */
982        public final <S extends Component, R> R visitChildren(final Class<?> clazz,
983                final IVisitor<S, R> visitor)
984        {
985                return Visits.visitChildren(this, visitor, new ClassVisitFilter(clazz));
986        }
987
988        /**
989         * Traverses all child components in this container, calling the visitor's visit method at each
990         * one.
991         * 
992         * @param <R>
993         * @param visitor
994         *            The visitor to call back to
995         * @return The return value from a visitor which halted the traversal, or null if the entire
996         *         traversal occurred
997         */
998        public final <R> R visitChildren(final IVisitor<Component, R> visitor)
999        {
1000                return Visits.visitChildren(this, visitor);
1001        }
1002
1003        /**
1004         * @param child
1005         *            Component being added
1006         */
1007        private void addedComponent(final Component child)
1008        {
1009                // Check for degenerate case
1010                Args.notNull(child, "child");
1011
1012                MarkupContainer parent = child.getParent();
1013                if (parent != null && parent != this)
1014                {
1015                        parent.remove(child);
1016                }
1017
1018                // Set child's parent
1019                child.setParent(this);
1020
1021                final DebugSettings debugSettings = Application.get().getDebugSettings();
1022                if (debugSettings.isLinePreciseReportingOnAddComponentEnabled()
1023                        && debugSettings.getComponentUseCheck())
1024                {
1025                        child.setMetaData(ADDED_AT_KEY,
1026                                ComponentStrings.toString(child, new MarkupException("added")));
1027                }
1028
1029                Page page = findPage();
1030
1031                if (page != null)
1032                {
1033                        // tell the page a component has been added first, to allow it to initialize
1034                        page.componentAdded(child);
1035
1036                        // initialize the component
1037                        if (page.isInitialized())
1038                        {
1039                                child.internalInitialize();
1040                        }
1041                }
1042
1043                // if the PREPARED_FOR_RENDER flag is set, we have already called
1044                // beforeRender on this component's children. So we need to initialize the newly added one
1045                if (isPreparedForRender())
1046                {
1047                        child.beforeRender();
1048                }
1049        }
1050
1051        /**
1052         * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL IT
1053         * 
1054         * Overrides {@link Component#internalInitialize()} to call {@link Component#fireInitialize()}
1055         * for itself and for all its children.
1056         * 
1057         * @see org.apache.wicket.Component#fireInitialize()
1058         */
1059        @Override
1060        public final void internalInitialize()
1061        {
1062                super.fireInitialize();
1063                visitChildren(new IVisitor<Component, Void>()
1064                {
1065                        @Override
1066                        public void component(final Component component, final IVisit<Void> visit)
1067                        {
1068                                component.fireInitialize();
1069                        }
1070                });
1071        }
1072
1073        /*
1074         * === Internal management for keeping track of child components ===
1075         * 
1076         * A markup container is the base component for containing child objects. It is one of the most
1077         * heavily used components so we should keep it's (memory and CPU) footprint small.
1078         * 
1079         * The goals for the internal management of the list of child components are:
1080         * 
1081         * - as low big-O complexity as possible, preferrably O(1)
1082         * 
1083         * - as low memory consumption as possible (don't use more memory than strictly necessary)
1084         * 
1085         * - ensure that iterating through the (list of) children be as consistent as possible
1086         * 
1087         * - retain the order of addition in the iteration
1088         * 
1089         * These goals are attained by storing the children in a single field that is implemented using:
1090         * 
1091         * - a component when there's only one child
1092         * 
1093         * - a list of components when there are more than 1 children
1094         * 
1095         * - a map of components when the number of children makes looking up children by id more costly
1096         * than an indexed search (see MAPIFY_THRESHOLD)
1097         * 
1098         * To ensure that iterating through the list of children keeps working even when children are
1099         * added, replaced and removed without throwing a ConcurrentModificationException a special
1100         * iterator is used. The markup container tracks removals from and additions to the children
1101         * during the request, enabling the iterator to skip over those items and adjust to changing
1102         * internal data structures.
1103         */
1104
1105        /**
1106         * A type washing accessor method for getting the children without having to cast the field
1107         * explicitly.
1108         * 
1109         * @return the children as a T
1110         */
1111        @SuppressWarnings("unchecked")
1112        private <T> T children()
1113        {
1114                return (T)children;
1115        }
1116
1117        /**
1118         * Gets the child with the given {@code childId}
1119         * 
1120         * @param childId
1121         *            the component identifier
1122         * @return The child component or {@code null} when no child with the given identifier exists
1123         */
1124        private Component children_get(final String childId)
1125        {
1126                if (children == null)
1127                {
1128                        return null;
1129                }
1130                if (children instanceof Component)
1131                {
1132                        Component child = children();
1133                        return child.getId().equals(childId) ? child : null;
1134                }
1135                if (children instanceof List)
1136                {
1137                        List<Component> kids = children();
1138                        for (Component child : kids)
1139                        {
1140                                if (child.getId().equals(childId))
1141                                {
1142                                        return child;
1143                                }
1144                        }
1145                        return null;
1146                }
1147                Map<String, Component> kids = children();
1148                return kids.get(childId);
1149        }
1150
1151        /**
1152         * Removes the child component identified by {@code childId} from the list of children.
1153         * 
1154         * Will change the internal list or map to a single component when the number of children hits
1155         * 1, but not change the internal map to a list when the threshold is reached (the memory was
1156         * already claimed, so there's little to be gained other than wasting CPU cycles for the
1157         * conversion).
1158         * 
1159         * @param childId
1160         *            the id of the child component to remove
1161         */
1162        private void children_remove(String childId)
1163        {
1164                if (children instanceof Component)
1165                {
1166                        Component oldChild = children();
1167                        if (oldChild.getId().equals(childId))
1168                        {
1169                                children = null;
1170                                removals_add(oldChild, null);
1171                        }
1172                }
1173                else if (children instanceof List)
1174                {
1175                        List<Component> childrenList = children();
1176                        Iterator<Component> it = childrenList.iterator();
1177                        Component prevChild = null;
1178                        while (it.hasNext())
1179                        {
1180                                Component child = it.next();
1181                                if (child.getId().equals(childId))
1182                                {
1183                                        it.remove();
1184                                        removals_add(child, prevChild);
1185                                        if (childrenList.size() == 1)
1186                                        {
1187                                                children = childrenList.get(0);
1188                                        }
1189                                        return;
1190                                }
1191                                prevChild = child;
1192                        }
1193                }
1194                else if (children instanceof LinkedMap)
1195                {
1196                        LinkedMap<String, Component> childrenMap = children();
1197                        if (childrenMap.containsKey(childId))
1198                        {
1199                                String prevSiblingId = childrenMap.previousKey(childId);
1200                                Component oldChild = childrenMap.remove(childId);
1201                                removals_add(oldChild, childrenMap.get(prevSiblingId));
1202                                if (childrenMap.size() == 1)
1203                                {
1204                                        children = childrenMap.values().iterator().next();
1205                                }
1206                        }
1207                }
1208        }
1209
1210        /**
1211         * Gets the number of child components of this markup container.
1212         * 
1213         * @return The number of children
1214         */
1215        private int children_size()
1216        {
1217                if (children == null)
1218                {
1219                        return 0;
1220                }
1221                if (children instanceof Component)
1222                {
1223                        return 1;
1224                }
1225                if (children instanceof List)
1226                {
1227                        List<?> kids = children();
1228                        return kids.size();
1229                }
1230                return ((Map<?, ?>)children).size();
1231        }
1232
1233        /**
1234         * Puts the {@code child} component into the list of children of this markup container. If a
1235         * component existed with the same {@code child.getId()} it is replaced and the old component is
1236         * returned.
1237         * 
1238         * When a component is replaced, the internal structure of the children is not modified, so we
1239         * don't have to update the internal {@link #modCounter} in those circumstances. When a
1240         * component is added, we do have to increase the {@link #modCounter} to notify iterators of
1241         * this change.
1242         * 
1243         * @param child
1244         *            The child
1245         * @return Any component that was replaced
1246         */
1247        private Component children_put(final Component child)
1248        {
1249                if (children == null)
1250                {
1251                        children = child;
1252
1253                        // it is an addtion, so we need to notify the iterators of this change.
1254                        modCounter++;
1255
1256                        return null;
1257                }
1258
1259                if (children instanceof Component)
1260                {
1261                        /* first see if the child replaces the existing child */
1262                        Component oldChild = children();
1263                        if (oldChild.getId().equals(child.getId()))
1264                        {
1265                                children = child;
1266                                return oldChild;
1267                        }
1268                        else
1269                        {
1270                                /*
1271                                 * the put doesn't replace the existing child, so we need to increase the children
1272                                 * storage to a list and add the existing and new child to it
1273                                 */
1274                                Component originalChild = children();
1275                                List<Component> newChildren = new ArrayList<>(INITIAL_CHILD_LIST_CAPACITY);
1276                                newChildren.add(originalChild);
1277                                newChildren.add(child);
1278                                children = newChildren;
1279
1280                                // it is an addtion, so we need to notify the iterators of this change.
1281                                modCounter++;
1282                                return null;
1283                        }
1284                }
1285
1286                if (children instanceof List)
1287                {
1288                        List<Component> childrenList = children();
1289
1290                        // first see if the child replaces an existing child
1291                        for (int i = 0; i < childrenList.size(); i++)
1292                        {
1293                                Component curChild = childrenList.get(i);
1294                                if (curChild.getId().equals(child.getId()))
1295                                {
1296                                        return childrenList.set(i, child);
1297                                }
1298                        }
1299
1300                        // it is an addtion, so we need to notify the iterators of this change.
1301                        modCounter++;
1302
1303                        /*
1304                         * If it still fits in the allotted number of items of a List, just add it, otherwise
1305                         * change the internal data structure to a Map for speedier lookups.
1306                         */
1307                        if (childrenList.size() < MAPIFY_THRESHOLD)
1308                        {
1309                                childrenList.add(child);
1310                        }
1311                        else
1312                        {
1313                                Map<String, Component> newChildren = new LinkedMap<>(MAPIFY_THRESHOLD * 2);
1314                                for (Component curChild : childrenList)
1315                                {
1316                                        newChildren.put(curChild.getId(), curChild);
1317                                }
1318                                newChildren.put(child.getId(), child);
1319                                children = newChildren;
1320                        }
1321                        return null;
1322                }
1323
1324                Map<String, Component> childrenMap = children();
1325                Component oldChild = childrenMap.put(child.getId(), child);
1326
1327                if (oldChild == null)
1328                {
1329                        // it is an addtion, so we need to notify the iterators of this change.
1330                        modCounter++;
1331                }
1332                return oldChild;
1333        }
1334
1335        /**
1336         * Retrieves the during the request removed children. These are stored in the metadata and
1337         * cleared at the end of the request {@link #onDetach()}
1338         * 
1339         * @return the list of removed children, may be {@code null}
1340         */
1341        private LinkedList<RemovedChild> removals_get()
1342        {
1343                return getRequestFlag(RFLAG_CONTAINER_HAS_REMOVALS) ? getMetaData(REMOVALS_KEY) : null;
1344        }
1345
1346        /**
1347         * Sets the during the request removed children. These are stored in the metadata and cleared at
1348         * the end of the request, see {@link #onDetach()}.
1349         * 
1350         * @param removals
1351         *            the new list of removals
1352         */
1353        private void removals_set(LinkedList<RemovedChild> removals)
1354        {
1355                setRequestFlag(RFLAG_CONTAINER_HAS_REMOVALS, removals != null);
1356                setMetaData(REMOVALS_KEY, removals);
1357        }
1358
1359        /**
1360         * Removes the list of removals from the metadata.
1361         */
1362        private void removals_clear()
1363        {
1364                if (getRequestFlag(RFLAG_CONTAINER_HAS_REMOVALS))
1365                {
1366                        removals_set(null);
1367                }
1368        }
1369
1370        /**
1371         * Adds the {@code removedChild} to the list of removals and links it to the
1372         * {@code previousSibling}
1373         * 
1374         * @param removedChild
1375         *            the child that was removed
1376         * @param prevSibling
1377         *            the child that was the previous sibling of the removed child
1378         */
1379        private void removals_add(Component removedChild, Component prevSibling)
1380        {
1381                modCounter++;
1382
1383                LinkedList<RemovedChild> removals = removals_get();
1384                if (removals == null)
1385                {
1386                        removals = new LinkedList<>();
1387                        removals_set(removals);
1388                }
1389                removals.add(new RemovedChild(removedChild, prevSibling));
1390        }
1391
1392        /**
1393         * @param component
1394         *            Component being removed
1395         */
1396        private void removedComponent(final Component component)
1397        {
1398                // Notify Page that component is being removed
1399                final Page page = component.findPage();
1400                if (page != null)
1401                {
1402                        page.componentRemoved(component);
1403                }
1404
1405                component.detach();
1406
1407                component.internalOnRemove();
1408
1409                // Component is removed
1410                component.setParent(null);
1411        }
1412
1413        /**
1414         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE OR OVERWRITE IT.
1415         * 
1416         * Renders the next element of markup in the given markup stream.
1417         * 
1418         * @param markupStream
1419         *            The markup stream
1420         * @return true, if element was rendered as RawMarkup
1421         */
1422        protected boolean renderNext(final MarkupStream markupStream)
1423        {
1424                // Get the current markup element
1425                final MarkupElement element = markupStream.get();
1426
1427                // If it's a tag like <wicket..> or <span wicket:id="..." >
1428                if ((element instanceof ComponentTag) && !markupStream.atCloseTag())
1429                {
1430                        // Get element as tag
1431                        final ComponentTag tag = (ComponentTag)element;
1432
1433                        if (tag instanceof WicketTag && ((WicketTag)tag).isFragmentTag()){
1434                                return false;
1435                        }
1436
1437                        // Get component id
1438                        final String id = tag.getId();
1439
1440                        // Get the component for the id from the given container
1441                        Component component = get(id);
1442
1443                        if (component == null)
1444                        {
1445                                component = ComponentResolvers.resolve(this, markupStream, tag, null);
1446                                if ((component != null) && (component.getParent() == null))
1447                                {
1448                                        autoAdd(component, markupStream);
1449                                }
1450                                else if (component != null)
1451                                {
1452                                        component.setMarkup(markupStream.getMarkupFragment());
1453                                }
1454                        }
1455                
1456                        // Failed to find it?
1457                        if (component != null)
1458                        {
1459                                component.render();
1460                        }
1461                        else if (tag.getFlag(ComponentTag.RENDER_RAW))
1462                        {
1463                                // No component found, but "render as raw markup" flag found
1464                                if (canRenderRawTag(tag))
1465                                {                                       
1466                                        getResponse().write(element.toCharSequence());
1467                                } 
1468                                return true;
1469                        }
1470                        else
1471                        {
1472                                throwException(markupStream, tag);
1473                        }
1474                }
1475                else
1476                {
1477                        // Render as raw markup
1478                        if (canRenderRawTag(element))
1479                        {
1480                                getResponse().write(element.toCharSequence());
1481                        }
1482                        return true;
1483                }
1484
1485                return false;
1486        }
1487        
1488        /**
1489         * Says if the given tag can be handled as a raw markup.
1490         * 
1491         * @param tag
1492         *                      the current tag.
1493         * @return true if the tag can be handled as raw markup, false otherwise.
1494         */
1495        private boolean canRenderRawTag(MarkupElement tag)
1496        {
1497                boolean isWicketTag = tag instanceof WicketTag;
1498                
1499                boolean stripTag = isWicketTag ? Application.get().getMarkupSettings().getStripWicketTags() : false; 
1500                
1501                return !stripTag;
1502        }
1503        
1504        /**
1505         * Throws a {@code org.apache.wicket.markup.MarkupException} when the
1506         * component markup is not consistent.
1507         * 
1508         * @param markupStream
1509         *                      the source stream for the component markup.
1510         * @param tag
1511         *                      the tag that can not be handled.
1512         */
1513        private void throwException(final MarkupStream markupStream, final ComponentTag tag)
1514        {
1515                final String id = tag.getId();
1516                
1517                if (tag instanceof WicketTag)
1518                {
1519                        if (((WicketTag)tag).isChildTag())
1520                        {
1521                                markupStream.throwMarkupException("Found " + tag.toString() +
1522                                        " but no <wicket:extend>. Container: " + toString());
1523                        }
1524                        else
1525                        {
1526                                markupStream.throwMarkupException("Failed to handle: " +
1527                                        tag.toString() +
1528                                        ". It might be that no resolver has been registered to handle this special tag. " +
1529                                        " But it also could be that you declared wicket:id=" + id +
1530                                        " in your markup, but that you either did not add the " +
1531                                        "component to your page at all, or that the hierarchy does not match. " +
1532                                        "Container: " + toString());
1533                        }
1534                }
1535
1536                List<String> names = findSimilarComponents(id);
1537
1538                // No one was able to handle the component id
1539                StringBuilder msg = new StringBuilder(500);
1540                msg.append("Unable to find component with id '");
1541                msg.append(id);
1542                msg.append("' in ");
1543                msg.append(this.toString());
1544                msg.append("\n\tExpected: '");
1545                msg.append(getPageRelativePath());
1546                msg.append(PATH_SEPARATOR);
1547                msg.append(id);
1548                msg.append("'.\n\tFound with similar names: '");
1549                msg.append(Strings.join("', ", names));
1550                msg.append('\'');
1551
1552                log.error(msg.toString());
1553                markupStream.throwMarkupException(msg.toString());
1554        }
1555
1556        private List<String> findSimilarComponents(final String id)
1557        {
1558                final List<String> names = Generics.newArrayList();
1559
1560                Page page = findPage();
1561                if (page != null)
1562                {
1563                        page.visitChildren(new IVisitor<Component, Void>()
1564                        {
1565                                @Override
1566                                public void component(Component component, IVisit<Void> visit)
1567                                {
1568                                        if (Strings.getLevenshteinDistance(id.toLowerCase(Locale.ROOT), component.getId()
1569                                                .toLowerCase(Locale.ROOT)) < 3)
1570                                        {
1571                                                names.add(component.getPageRelativePath());
1572                                        }
1573                                }
1574                        });
1575                }
1576
1577                return names;
1578        }
1579
1580        /**
1581         * Handle the container's body. If your override of this method does not advance the markup
1582         * stream to the close tag for the openTag, a runtime exception will be thrown by the framework.
1583         * 
1584         * @param markupStream
1585         *            The markup stream
1586         * @param openTag
1587         *            The open tag for the body
1588         */
1589        @Override
1590        public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
1591        {
1592                renderComponentTagBody(markupStream, openTag);
1593        }
1594
1595        @Override
1596        protected void onRender()
1597        {
1598                internalRenderComponent();
1599        }
1600
1601        /**
1602         * Renders markup for the body of a ComponentTag from the current position in the given markup
1603         * stream. If the open tag passed in does not require a close tag, nothing happens. Markup is
1604         * rendered until the closing tag for openTag is reached.
1605         * 
1606         * @param markupStream
1607         *            The markup stream
1608         * @param openTag
1609         *            The open tag
1610         */
1611        private void renderComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
1612        {
1613                if ((markupStream != null) && (markupStream.getCurrentIndex() > 0))
1614                {
1615                        // If the original tag has been changed from open-close to open-body-close, than we are
1616                        // done. Other components, e.g. BorderBody, rely on this method being called.
1617                        ComponentTag origOpenTag = (ComponentTag)markupStream.get(markupStream.getCurrentIndex() - 1);
1618                        if (origOpenTag.isOpenClose())
1619                        {
1620                                return;
1621                        }
1622                }
1623
1624                // If the open tag requires a close tag
1625                boolean render = openTag.requiresCloseTag();
1626                if (render == false)
1627                {
1628                        // Tags like <p> do not require a close tag, but they may have.
1629                        render = !openTag.hasNoCloseTag();
1630                }
1631
1632                if (render)
1633                {
1634                        renderAll(markupStream, openTag);
1635                }
1636        }
1637
1638        /**
1639         * Loop through the markup in this container
1640         * 
1641         * @param markupStream
1642         * @param openTag
1643         */
1644        protected final void renderAll(final MarkupStream markupStream, final ComponentTag openTag)
1645        {
1646                while (markupStream.isCurrentIndexInsideTheStream())
1647                {
1648                        // In case of Page we need to render the whole file. For all other components just what
1649                        // is in between the open and the close tag.
1650                        if ((openTag != null) && markupStream.get().closes(openTag))
1651                        {
1652                                break;
1653                        }
1654
1655                        // Remember where we are
1656                        final int index = markupStream.getCurrentIndex();
1657
1658                        // Render the markup element
1659                        boolean rawMarkup = renderNext(markupStream);
1660
1661                        // Go back to where we were and move the markup stream forward to whatever the next
1662                        // element is.
1663                        markupStream.setCurrentIndex(index);
1664
1665                        if (rawMarkup)
1666                        {
1667                                markupStream.next();
1668                        }
1669                        else if (!markupStream.getTag().isClose())
1670                        {
1671                                markupStream.skipComponent();
1672                        }
1673                        else
1674                        {
1675                                throw new WicketRuntimeException("Ups. This should never happen. " +
1676                                        markupStream.toString());
1677                        }
1678                }
1679        }
1680
1681        @Override
1682        void removeChildren()
1683        {
1684                super.removeChildren();
1685
1686                for (Component component : this)
1687                {
1688                        component.internalOnRemove();
1689                }
1690        }
1691
1692        @Override
1693        void detachChildren()
1694        {
1695                super.detachChildren();
1696
1697                for (Component component : this)
1698                {
1699                        component.detach();
1700                }
1701        }
1702
1703        /**
1704         * 
1705         * @see org.apache.wicket.Component#internalMarkRendering(boolean)
1706         */
1707        @Override
1708        void internalMarkRendering(boolean setRenderingFlag)
1709        {
1710                super.internalMarkRendering(setRenderingFlag);
1711
1712                for (Component child : this)
1713                {
1714                        child.internalMarkRendering(setRenderingFlag);
1715                }
1716        }
1717
1718        /**
1719         * @return a copy of the children array.
1720         */
1721        @SuppressWarnings("unchecked")
1722        private List<Component> copyChildren()
1723        {
1724                if (children == null)
1725                {
1726                        return Collections.emptyList();
1727                }
1728                else if (children instanceof Component)
1729                {
1730                        return Collections.singletonList((Component)children);
1731                }
1732                else if (children instanceof List)
1733                {
1734                        return new ArrayList<>((List<Component>)children);
1735                }
1736                else
1737                {
1738                        return new ArrayList<>(((Map<String, Component>)children).values());
1739                }
1740        }
1741
1742        @Override
1743        void onBeforeRenderChildren()
1744        {
1745                super.onBeforeRenderChildren();
1746
1747                try
1748                {
1749                        // Loop through child components
1750                        for (final Component child : this)
1751                        {
1752                                // Get next child
1753                                // Call begin request on the child
1754                                // We need to check whether the child's wasn't removed from the
1755                                // component in the meanwhile (e.g. from another's child
1756                                // onBeforeRender)
1757                                if (child.getParent() == this)
1758                                {
1759                                        child.beforeRender();
1760                                }
1761                        }
1762                }
1763                catch (RuntimeException ex)
1764                {
1765                        if (ex instanceof WicketRuntimeException)
1766                        {
1767                                throw ex;
1768                        }
1769                        else
1770                        {
1771                                throw new WicketRuntimeException("Error attaching this container for rendering: " +
1772                                        this, ex);
1773                        }
1774                }
1775        }
1776
1777        @Override
1778        void onEnabledStateChanged()
1779        {
1780                super.onEnabledStateChanged();
1781                visitChildren(new IVisitor<Component, Void>()
1782                {
1783                        @Override
1784                        public void component(Component component, IVisit<Void> visit)
1785                        {
1786                                component.clearEnabledInHierarchyCache();
1787                        }
1788                });
1789        }
1790
1791        @Override
1792        void onVisibleStateChanged()
1793        {
1794                super.onVisibleStateChanged();
1795                visitChildren(new IVisitor<Component, Void>()
1796                {
1797                        @Override
1798                        public void component(Component component, IVisit<Void> visit)
1799                        {
1800                                component.clearVisibleInHierarchyCache();
1801                        }
1802                });
1803        }
1804
1805        @Override
1806        protected void onDetach()
1807        {
1808                super.onDetach();
1809
1810                modCounter++;
1811                removals_clear();
1812
1813                if (queue != null && !queue.isEmpty() && hasBeenRendered())
1814                {
1815                        throw new WicketRuntimeException(
1816                                        String.format("Detach called on component with id '%s' while it had a non-empty queue: %s",
1817                                                        getId(), queue));
1818                }
1819        }
1820
1821        private transient ComponentQueue queue;
1822
1823        /**
1824         * Queues one or more components to be dequeued later. The advantage of this method over the
1825         * {@link #add(Component...)} method is that the component does not have to be added to its
1826         * direct parent, only to a parent upstream; it will be dequeued into the correct parent using
1827         * the hierarchy defined in the markup. This allows the component hierarchy to be maintained only
1828         * in markup instead of in markup and in java code; affording designers and developers more
1829         * freedom when moving components in markup.
1830         * 
1831         * @param components
1832         *             the components to queue
1833         * @return {@code this} for method chaining             
1834         */
1835        public MarkupContainer queue(Component... components)
1836        {
1837                if (queue == null)
1838                {
1839                        queue = new ComponentQueue();
1840                }
1841                queue.add(components);
1842                
1843                Page page = findPage();
1844
1845                if (page != null)
1846                {
1847                        dequeue();                      
1848                }
1849
1850                return this;
1851        }
1852
1853        /**
1854         * @see IQueueRegion#dequeue()
1855         */
1856        public void dequeue()
1857        {
1858                if (this instanceof IQueueRegion)
1859                {
1860                        DequeueContext dequeue = newDequeueContext();
1861                        dequeuePreamble(dequeue);
1862                }
1863                else
1864                {
1865                        MarkupContainer queueRegion = (MarkupContainer)findParent(IQueueRegion.class);
1866
1867                        if (queueRegion == null)
1868                        {
1869                                return;
1870                        }
1871                        
1872                        MarkupContainer anchestor = this;
1873                        boolean hasQueuedChildren = !isQueueEmpty();
1874                        
1875                        while (!hasQueuedChildren && anchestor != queueRegion)
1876                        {
1877                                anchestor = anchestor.getParent();
1878                                hasQueuedChildren = !anchestor.isQueueEmpty();
1879                        }
1880                        
1881                        if (hasQueuedChildren && !queueRegion.getRequestFlag(RFLAG_CONTAINER_DEQUEING))
1882                        {
1883                                queueRegion.dequeue();
1884                        }
1885                }
1886        }
1887
1888        @Override
1889        protected void onInitialize()
1890        {
1891                super.onInitialize();
1892                dequeue();
1893        }
1894        /**
1895         * @return {@code true} when one or more components are queued
1896         */
1897        private boolean isQueueEmpty()
1898        {
1899                return queue == null || queue.isEmpty();
1900        }
1901
1902        /**
1903         * @return {@code true} when this markup container is a queue region
1904         */
1905        private boolean isQueueRegion() 
1906        {
1907                return IQueueRegion.class.isInstance(this);
1908        }
1909
1910        /**
1911         * Run preliminary operations before running {@link #dequeue(DequeueContext)}. More in detail it
1912         * throws an exception if the container is already dequeuing, and it also takes care of setting
1913         * flag {@code RFLAG_CONTAINER_DEQUEING} to true before running {@link #dequeue(DequeueContext)}
1914         * and setting it back to false after dequeuing is completed.
1915         * 
1916         * @param dequeue
1917         *            the dequeue context to use
1918         */
1919        protected void dequeuePreamble(DequeueContext dequeue)
1920        {
1921                if (getRequestFlag(RFLAG_CONTAINER_DEQUEING))
1922                {
1923                        throw new IllegalStateException("This container is already dequeing: " + this);
1924                }
1925
1926                setRequestFlag(RFLAG_CONTAINER_DEQUEING, true);
1927                try
1928                {
1929                        if (dequeue == null)
1930                        {
1931                                return;
1932                        }
1933
1934                        if (dequeue.peekTag() != null)
1935                        {
1936                                dequeue(dequeue);
1937                        }
1938                }
1939                finally
1940                {
1941                        setRequestFlag(RFLAG_CONTAINER_DEQUEING, false);
1942                }
1943        }
1944
1945        /**
1946         * Dequeues components. The default implementation iterates direct children of this container
1947         * found in its markup and tries to find matching
1948         * components in queues filled by a call to {@link #queue(Component...)}. It then delegates the
1949         * dequeueing to these children.
1950         * 
1951         * 
1952         * Certain components that implement custom markup behaviors (such as repeaters and borders)
1953         * override this method to bring dequeueing in line with their custom markup handling.
1954         * 
1955         * @param dequeue
1956         *             the dequeue context to use     
1957         */
1958        public void dequeue(DequeueContext dequeue)
1959        {
1960                while (dequeue.isAtOpenOrOpenCloseTag())
1961                {
1962                        ComponentTag tag = dequeue.takeTag();
1963        
1964                        // see if child is already added to parent
1965                        Component child = findChildComponent(tag);
1966
1967                        if (child == null)
1968                        {
1969                                // the container does not yet have a child with this id, see if we can
1970                                // dequeue
1971                                child = dequeue.findComponentToDequeue(tag);
1972                                
1973                                //if tag has an autocomponent factory let's use it
1974                                if (child == null && tag.getAutoComponentFactory() != null)
1975                                {
1976                                        IAutoComponentFactory autoComponentFactory = tag.getAutoComponentFactory();
1977                                        child = autoComponentFactory.newComponent(this, tag);
1978                                }
1979                                
1980                                if (child != null)
1981                                {
1982                                        addDequeuedComponent(child, tag);
1983                                }
1984                        }
1985                        
1986                        if (tag.isOpen() && !tag.hasNoCloseTag())
1987            {
1988                            dequeueChild(child, tag, dequeue);
1989            }
1990                }
1991
1992        }
1993
1994        /**
1995         * Search the child component for the given tag.
1996         * 
1997         * @param tag
1998         *                              the component tag 
1999         * @return the child component for the given tag or null if no child can not be found.
2000         */
2001        protected Component findChildComponent(ComponentTag tag)
2002        {
2003                return get(tag.getId());
2004        }
2005        
2006        /**
2007         * Propagates dequeuing to child component.
2008         * 
2009         * @param child
2010         *             the child component
2011         * @param tag
2012         *             the child tag
2013         * @param dequeue
2014         *             the dequeue context to use
2015         */
2016        private void dequeueChild(Component child, ComponentTag tag, DequeueContext dequeue)
2017        {
2018                ChildToDequeueType childType = ChildToDequeueType.fromChild(child);
2019                
2020                if (childType == ChildToDequeueType.QUEUE_REGION ||
2021                        childType == ChildToDequeueType.BORDER)
2022                {
2023                        ((IQueueRegion)child).dequeue();                        
2024                }
2025                
2026                if (childType == ChildToDequeueType.BORDER) 
2027                {
2028            Border childContainer = (Border)child;
2029            // propagate dequeuing to border's body
2030            MarkupContainer body = childContainer.getBodyContainer();
2031
2032            dequeueChildrenContainer(dequeue, body);
2033                }
2034                
2035                if (childType == ChildToDequeueType.MARKUP_CONTAINER)
2036                {
2037            // propagate dequeuing to containers
2038            MarkupContainer childContainer = (MarkupContainer)child;
2039
2040            dequeueChildrenContainer(dequeue, childContainer);                  
2041                }
2042                
2043                if (childType == ChildToDequeueType.NULL || 
2044                        childType == ChildToDequeueType.QUEUE_REGION)
2045                {
2046                        dequeue.skipToCloseTag();
2047                }
2048
2049                // pull the close tag off
2050                ComponentTag close = dequeue.takeTag();
2051                do
2052                {
2053                        if (close != null && close.closes(tag))
2054                        {
2055                                return;
2056                        }
2057                } while ((close = dequeue.takeTag()) != null);
2058
2059                throw new IllegalStateException(String.format("Could not find the closing tag for '%s'", tag));
2060        }
2061
2062    private void dequeueChildrenContainer(DequeueContext dequeue, MarkupContainer child)
2063    {
2064        dequeue.pushContainer(child);
2065        child.dequeue(dequeue);
2066        dequeue.popContainer();
2067    }
2068
2069    /** @see IQueueRegion#newDequeueContext() */
2070        public DequeueContext newDequeueContext()
2071        {
2072                IMarkupFragment markup = getRegionMarkup();
2073                if (markup == null)
2074                {
2075                        return null;
2076                }
2077
2078                return new DequeueContext(markup, this, false);
2079        }
2080
2081        /** @see IQueueRegion#getRegionMarkup() */
2082        public IMarkupFragment getRegionMarkup()
2083        {
2084                return getAssociatedMarkup();
2085        }
2086
2087        /**
2088         * Checks if this container can dequeue a child represented by the specified tag. This method
2089         * should be overridden when containers can dequeue components represented by non-standard tags.
2090         * For example, borders override this method and dequeue their body container when processing
2091         * the body tag.
2092         * 
2093         * By default all {@link ComponentTag}s are supported as well as {@link WicketTag}s that return
2094         * a non-null value from {@link WicketTag#getAutoComponentFactory()} method.
2095         * 
2096         * @param tag
2097         */
2098        protected DequeueTagAction canDequeueTag(ComponentTag tag)
2099        {
2100                if (tag instanceof WicketTag)
2101                {
2102                        WicketTag wicketTag = (WicketTag)tag;
2103                        if (wicketTag.isContainerTag())
2104                        {
2105                                return DequeueTagAction.DEQUEUE;
2106                        }
2107                        else if (wicketTag.getAutoComponentFactory() != null)
2108                        {
2109                                return DequeueTagAction.DEQUEUE;
2110                        }
2111                        else if (wicketTag.isFragmentTag())
2112                        {
2113                                return DequeueTagAction.SKIP;
2114                        }
2115                        else if (wicketTag.isChildTag())
2116                        {
2117                                return DequeueTagAction.IGNORE;
2118                        }
2119                        else if (wicketTag.isHeadTag())
2120                        {
2121                                return DequeueTagAction.SKIP;
2122                        }
2123                        else if (wicketTag.isLinkTag())
2124                        {
2125                                return DequeueTagAction.DEQUEUE;
2126                        }
2127                        else
2128                        {
2129                                return null; // don't know
2130                        }
2131                }
2132                
2133                //if is a label tag, ignore it
2134                if (tag.isAutoComponentTag() 
2135                        && tag.getId().startsWith(AutoLabelResolver.LABEL_ATTR))
2136                {
2137                        return DequeueTagAction.IGNORE;
2138                }
2139                
2140                return DequeueTagAction.DEQUEUE;
2141        }
2142
2143        /**
2144         * Queries this container to find a child that can be dequeued that matches the specified tag.
2145         * The default implementation will check if there is a component in the queue that has the same
2146         * id as a tag, but sometimes custom tags can be dequeued and in those situations this method
2147         * should be overridden.
2148         * 
2149         * @param tag
2150         * @return
2151         */
2152        public Component findComponentToDequeue(ComponentTag tag)
2153        {
2154                return queue == null ? null : queue.remove(tag.getId());
2155        }
2156
2157        /**
2158         * Adds a dequeued component to this container. This method should rarely be overridden because
2159         * the common case of simply forwarding the component to
2160         * {@link MarkupContainer#add(Component...)} method should cover most cases. Components that
2161         * implement a custom hierarchy, such as borders, may wish to override it to support edge-case
2162         * non-standard behavior.
2163         * 
2164         * @param component
2165         * @param tag
2166         */
2167        protected void addDequeuedComponent(Component component, ComponentTag tag)
2168        {
2169                add(component);
2170        }
2171
2172        /**
2173         * Returns a sequential {@code Stream} with the direct children of this markup container as its
2174         * source. This stream doesn't traverse the component tree.
2175         *
2176         * @return a sequential {@code Stream} over the direct children of this markup container
2177         * @since 8.0
2178         */
2179        public Stream<Component> stream()
2180        {
2181                return StreamSupport.stream(spliterator(), false);
2182        }
2183
2184        /**
2185         * Returns a sequential {@code Stream} with the all children of this markup container as its
2186         * source. This stream does traverse the component tree.
2187         * 
2188         * @return a sequential {@code Stream} over the all children of this markup container
2189         * @since 8.0
2190         */
2191        @SuppressWarnings("unchecked")
2192        public Stream<Component> streamChildren()
2193        {
2194                class ChildrenIterator<C> implements Iterator<C>
2195                {
2196                        private Iterator<C> currentIterator;
2197
2198                        private Deque<Iterator<C>> iteratorStack = new ArrayDeque<>();
2199
2200                        private ChildrenIterator(Iterator<C> iterator)
2201                        {
2202                                currentIterator = iterator;
2203                        }
2204
2205                        @Override
2206                        public boolean hasNext()
2207                        {
2208                                while (!currentIterator.hasNext() && !iteratorStack.isEmpty())
2209                                {
2210                                        currentIterator = iteratorStack.pop();
2211                                }
2212                                return currentIterator.hasNext();
2213                        }
2214
2215                        @Override
2216                        public C next()
2217                        {
2218                                C child = currentIterator.next();
2219                                if (child instanceof Iterable)
2220                                {
2221                                        iteratorStack.push(currentIterator);
2222                                        currentIterator = ((Iterable<C>)child).iterator();
2223                                }
2224                                return child;
2225                        }
2226                }
2227                return StreamSupport.stream(
2228                        Spliterators.spliteratorUnknownSize(new ChildrenIterator<>(iterator()), 0), false);
2229        }
2230}