dequeue docs
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/fd7ab849 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/fd7ab849 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/fd7ab849 Branch: refs/heads/master Commit: fd7ab849202144b183c3cbd69c81b7e4e433be99 Parents: 2e57d8d Author: Igor Vaynberg <igor.vaynb...@gmail.com> Authored: Sat Feb 15 21:56:18 2014 -0800 Committer: Igor Vaynberg <igor.vaynb...@gmail.com> Committed: Thu Feb 20 23:37:16 2014 -0800 ---------------------------------------------------------------------- .../java/org/apache/wicket/DequeueContext.java | 68 ++++++- .../java/org/apache/wicket/IQueueRegion.java | 22 ++- .../java/org/apache/wicket/MarkupContainer.java | 190 ++++++++++--------- .../wicket/markup/html/border/Border.java | 8 +- .../markup/repeater/AbstractRepeater.java | 4 + 5 files changed, 188 insertions(+), 104 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/fd7ab849/wicket-core/src/main/java/org/apache/wicket/DequeueContext.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/DequeueContext.java b/wicket-core/src/main/java/org/apache/wicket/DequeueContext.java index 4e62c3e..c13b0f2 100644 --- a/wicket-core/src/main/java/org/apache/wicket/DequeueContext.java +++ b/wicket-core/src/main/java/org/apache/wicket/DequeueContext.java @@ -2,19 +2,26 @@ package org.apache.wicket; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.IMarkupFragment; -import org.apache.wicket.markup.Markup; import org.apache.wicket.markup.MarkupElement; import org.apache.wicket.util.collections.ArrayListStack; -public class DequeueContext +/** + * Context for component dequeueing. Keeps track of markup position and container stack. + * + * @author igor + * + */ +public final class DequeueContext { private final IMarkupFragment markup; private int index; private ComponentTag next; private ArrayListStack<ComponentTag> tags = new ArrayListStack<>(); + private ArrayListStack<MarkupContainer> containers = new ArrayListStack<>(); - public static class Bookmark + /** A bookmark for the DequeueContext stack */ + public static final class Bookmark { private final int index; private final ComponentTag next; @@ -45,21 +52,39 @@ public class DequeueContext next=nextTag(); } + /** + * Saves the state of the context into a bookmark which can later be used to restore it. + */ public Bookmark save() { return new Bookmark(this); } + /** + * Restores the state of the context from the bookmark + * + * @param bookmark + */ public void restore(Bookmark bookmark) { bookmark.restore(this); } + /** + * Peeks markup tag that would be retrieved by call to {@link #popTag()} + * + * @return + */ public ComponentTag peekTag() { return next; } + /** + * Retrieves the next markup tag + * + * @return + */ public ComponentTag popTag() { ComponentTag taken=next; @@ -68,6 +93,9 @@ public class DequeueContext return taken; } + /** + * Skips to the closing tag of the tag retrieved from last call to {@link #popTag()} + */ public void skipToCloseTag() { if (tags.peek().isOpen()) @@ -89,7 +117,7 @@ public class DequeueContext { ComponentTag tag = (ComponentTag)element; ComponentTag open = tag.isClose() ? tag.getOpenTag() : tag; - if (canDequeue(open)) + if (canDequeueTag(open)) { index++; return tag; @@ -99,7 +127,7 @@ public class DequeueContext return null; } - private boolean canDequeue(ComponentTag open) + private boolean canDequeueTag(ComponentTag open) { if (containers.size() < 1) { @@ -108,7 +136,7 @@ public class DequeueContext } for (int i = containers.size() - 1; i >= 0; i--) { - if (containers.get(i).supportsDequeueingFrom((open))) + if (containers.get(i).canDequeueTag((open))) { return true; } @@ -116,27 +144,53 @@ public class DequeueContext return false; } + /** + * Checks if the tag returned by {@link #peekTag()} is either open or open-close. + * + * @return + */ public boolean isAtOpenOrOpenCloseTag() { return peekTag() != null && (peekTag().isOpen() || peekTag().isOpenClose()); } + /** + * Retrieves the container on the top of the containers stack + * + * @return + */ public MarkupContainer peekContainer() { return containers.peek(); } + /** + * Pushes a container onto the container stack + * + * @param container + */ public void pushContainer(MarkupContainer container) { containers.push(container); } + /** + * Pops a container from the container stack + * + * @return + */ public MarkupContainer popContainer() { return containers.pop(); } - public Component dequeue(ComponentTag tag) + /** + * Searches the container stack for a component that can be dequeud + * + * @param tag + * @return + */ + public Component findComponentToDequeue(ComponentTag tag) { for (int j = containers.size() - 1; j >= 0; j--) { http://git-wip-us.apache.org/repos/asf/wicket/blob/fd7ab849/wicket-core/src/main/java/org/apache/wicket/IQueueRegion.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/IQueueRegion.java b/wicket-core/src/main/java/org/apache/wicket/IQueueRegion.java index 86e30c6..beebdda 100644 --- a/wicket-core/src/main/java/org/apache/wicket/IQueueRegion.java +++ b/wicket-core/src/main/java/org/apache/wicket/IQueueRegion.java @@ -16,9 +16,11 @@ */ package org.apache.wicket; +import org.apache.wicket.markup.IMarkupFragment; + /** - * Demarcates components that can dequeue children. These are usually components with associated - * markup since the markup is needed to dequeue. + * Demarcates components that act as a root can dequeue children. These are usually components with + * associated markup since the markup is needed to dequeue. * * It is also important to note that components queued outside of a region cannot be dequeued into * it since regions act as roots for the dequeue process because they contain the markup. As such, @@ -30,5 +32,21 @@ package org.apache.wicket; */ public interface IQueueRegion { + /** + * Gets the markup that will be used to dequeue components in this container. Usually containers + * will return their associated markup by simply delegating to + * {@link MarkupContainer#getAssociatedMarkup()}, but compoennts that do not render markup in a + * standard way (such as repeaters and borders) may choose to override this method to implement + * custom behavior for the dequeuing process. + */ + public IMarkupFragment getDequeueMarkup(); + /** + * Starts component dequeueing on this {@link IQueueRegion}. This is the entry point into the + * dequeuing process, it creates the {@link DequeueContext} and delegates the opreation to the + * {@link #dequeue(DequeueContext)} method which performs the actual dequeuing. The context's + * markup is retrieved using the {@link #getDequeueMarkup()} method which allows subclasses to + * provide dequeueing-specific markup. + */ + public void dequeue(); } http://git-wip-us.apache.org/repos/asf/wicket/blob/fd7ab849/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java b/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java index e213f35..3fe7c98 100644 --- a/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java +++ b/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java @@ -36,13 +36,11 @@ import org.apache.wicket.markup.MarkupType; import org.apache.wicket.markup.WicketTag; import org.apache.wicket.markup.html.border.Border; import org.apache.wicket.markup.html.internal.InlineEnclosure; -import org.apache.wicket.markup.repeater.AbstractRepeater; import org.apache.wicket.markup.resolver.ComponentResolvers; import org.apache.wicket.model.IComponentInheritedModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.IWrapModel; import org.apache.wicket.settings.DebugSettings; -import org.apache.wicket.util.collections.ArrayListStack; import org.apache.wicket.util.io.IClusterable; import org.apache.wicket.util.iterator.ComponentHierarchyIterator; import org.apache.wicket.util.lang.Args; @@ -1519,22 +1517,28 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp super.onInitialize(); if (this instanceof IQueueRegion) { - // dequeue auto components - Markup markup = getAssociatedMarkup(); - if (markup != null) + // if this container is a queue region dequeue any queued up auto components + dequeueAutoComponents(); + } + } + + private void dequeueAutoComponents() + { + // dequeue auto components + IMarkupFragment markup = getDequeueMarkup(); + if (markup != null) + { + // make sure we have markup, when running inside tests we wont + for (int i = 0; i < markup.size(); i++) { - // make sure we have markup, when running inside tests we wont - for (int i = 0; i < markup.size(); i++) + MarkupElement element = markup.get(i); + if (element instanceof ComponentTag) { - MarkupElement element = markup.get(i); - if (element instanceof ComponentTag) + ComponentTag tag = (ComponentTag)element; + if (tag.getAutoComponentFactory() != null) { - ComponentTag tag = (ComponentTag)element; - if (tag.getAutoComponentFactory() != null) - { - Component auto = tag.getAutoComponentFactory().newComponent(tag); - queue(auto); - } + Component auto = tag.getAutoComponentFactory().newComponent(tag); + queue(auto); } } } @@ -1952,55 +1956,6 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp } } - /** - * Automatically create components for <wicket:xxx> tag. - */ - // to use it call it from #onInitialize() - private void createAndAddComponentsForWicketTags() - { - // Markup must be available - IMarkupFragment markup = getMarkup(); - if ((markup != null) && (markup.size() > 1)) - { - MarkupStream stream = new MarkupStream(markup); - - // Skip the first component tag which already belongs to 'this' container - if (stream.skipUntil(ComponentTag.class)) - { - stream.next(); - } - - // Search for <wicket:xxx> in the remaining markup and try to resolve the component - while (stream.skipUntil(ComponentTag.class)) - { - ComponentTag tag = stream.getTag(); - if (tag.isOpen() || tag.isOpenClose()) - { - if (tag instanceof WicketTag) - { - Component component = ComponentResolvers.resolve(this, stream, tag, null); - if ((component != null) && (component.getParent() == null)) - { - if (component.getId().equals(tag.getId()) == false) - { - // make sure we are able to get() the component during rendering - tag.setId(component.getId()); - tag.setModified(true); - } - add(component); - } - } - - if (tag.isOpen()) - { - stream.skipToMatchingCloseTag(tag); - } - } - stream.next(); - } - } - } - @Override protected void onDetach() { @@ -2017,6 +1972,17 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp private transient ComponentQueue queue; + /** + * Queues a component to be dequeued later. The advantage of this method over the + * {@link #add(Component...)} method is that the component does not have to be added to its + * direct parent, only to a parent upstream; it will be dequeued into the correct parent using + * the hierarchy defined in the markup. This allows the component hiearchy to be maintined only + * in markup instead of in markup and in java code; affording designers and developers more + * freedom when moving components in markup. + * + * @param components + * @return + */ public MarkupContainer queue(Component... components) { if (queue == null) @@ -2054,7 +2020,10 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp return this; } - void dequeue() + /** + * @see IQueueRegion#dequeue() + */ + public void dequeue() { if (!(this instanceof IQueueRegion)) { @@ -2070,7 +2039,20 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp setRequestFlag(RFLAG_CONTAINER_DEQUEING, true); try { - internalDequeue(); + IMarkupFragment markup = getDequeueMarkup(); + if (markup == null) + { + // markup not found, skip dequeuing + // this sometimes happens when we are in a unit test + return; + } + + DequeueContext dequeue = new DequeueContext(markup, this); + + if (dequeue.peekTag() != null) + { + dequeue(dequeue); + } } finally { @@ -2078,29 +2060,23 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp } } - private void internalDequeue() - { - IMarkupFragment markup = getDequeueMarkup(); - if (markup == null) - { - // markup not found, skip dequeuing - // this sometimes happens when we are in a unit test - return; - } - - DequeueContext dequeue = new DequeueContext(markup, this); - - if (dequeue.peekTag() != null) - { - dequeue(dequeue); - } - - - } + /** + * Dequeues components. The default implementation iterates direct children of this container + * found in the markup (retrieved via {@link #getDequeueMarkup()}) and tries to find matching + * components in queues filled by a call to {@link #queue(Component...)}. It then delegates the + * dequeing to these children. + * + * The provided {@link DequeueContext} is used to maintain the place in markup as well as the + * stack of components whose queues will be searched. For example, before delegating the call to + * a child the container will push the child onto the stack of components. + * + * Certain components that implement custom markup behaviors (such as repeaters and borders) + * override this method to bring dequeuing in line with their custom markup handling. + * + * @param dequeue + */ public void dequeue(DequeueContext dequeue) { - - while (dequeue.isAtOpenOrOpenCloseTag()) { @@ -2117,7 +2093,7 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp // the container does not yet have a child with this id, see if we can // dequeue - child = dequeue.dequeue(tag); + child = dequeue.findComponentToDequeue(tag); if (child != null) { @@ -2154,17 +2130,28 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp throw new IllegalStateException(); } } - - } } - protected IMarkupFragment getDequeueMarkup() { + /** @see IQueueRegion#getDequeueMarkup() */ + public IMarkupFragment getDequeueMarkup() + { return getAssociatedMarkup(); } - protected boolean supportsDequeueingFrom(ComponentTag tag) { + /** + * Checks if this container can dequeue a child represented by the specified tag. This method + * should be overridden when containers can dequeue components represented by non-standard tags. + * For example, borders override this method and dequeue their body container when processing + * the body tag. + * + * By default all {@link ComponentTag}s are supported as well as {@link WicketTag}s that return + * a non-null value from {@link WicketTag#getAutoComponentFactory()} method. + * + * @param tag + */ + protected boolean canDequeueTag(ComponentTag tag) { if (tag instanceof WicketTag) { WicketTag wicketTag=(WicketTag)tag; if (wicketTag.getAutoComponentFactory() != null) @@ -2177,11 +2164,30 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp return true; } + /** + * Queries this container to find a child that can be dequeued that matches the specified tag. + * The default implementation will check if there is a component in the queue that has the same + * id as a tag, but sometimes custom tags can be dequeued and in those situations this method + * should be overridden. + * + * @param tag + * @return + */ public Component findComponentToDequeue(ComponentTag tag) { return queue == null ? null : queue.remove(tag.getId()); } + /** + * Adds a dequeued component to this container. This method should rarely be overridden becase + * the common case of simply forwarding the component to + * {@link MarkupContainer#add(Component...))} method should cover most cases. Components that + * implement a custom hierarchy, such as borders, may wish to override it to support edge-case + * non-standard behavior. + * + * @param component + * @param tag + */ protected void addDequeuedComponent(Component component, ComponentTag tag) { add(component); } http://git-wip-us.apache.org/repos/asf/wicket/blob/fd7ab849/wicket-core/src/main/java/org/apache/wicket/markup/html/border/Border.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/border/Border.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/border/Border.java index 54fa141..e916d18 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/border/Border.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/border/Border.java @@ -537,7 +537,8 @@ public abstract class Border extends WebMarkupContainer implements IComponentRes return markup.find(child.getId()); } - protected IMarkupFragment getDequeueMarkup() { + public IMarkupFragment getDequeueMarkup() + { Border border=findParent(Border.class); IMarkupFragment fragment=findParent(Border.class).getMarkup(); /* @@ -614,12 +615,12 @@ public abstract class Border extends WebMarkupContainer implements IComponentRes @Override - protected boolean supportsDequeueingFrom(ComponentTag tag) { + protected boolean canDequeueTag(ComponentTag tag) { if ((tag instanceof WicketTag)&&((WicketTag)tag).isBodyTag()) { return true; } - return super.supportsDequeueingFrom(tag); + return super.canDequeueTag(tag); } @Override @@ -632,6 +633,7 @@ public abstract class Border extends WebMarkupContainer implements IComponentRes @Override protected void addDequeuedComponent(Component component, ComponentTag tag) { + // components queued in border get dequeued into the border not into the body container addToBorder(component); } } http://git-wip-us.apache.org/repos/asf/wicket/blob/fd7ab849/wicket-core/src/main/java/org/apache/wicket/markup/repeater/AbstractRepeater.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/repeater/AbstractRepeater.java b/wicket-core/src/main/java/org/apache/wicket/markup/repeater/AbstractRepeater.java index 134e239..2b1a4bb 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/repeater/AbstractRepeater.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/repeater/AbstractRepeater.java @@ -157,6 +157,10 @@ public abstract class AbstractRepeater extends WebMarkupContainer @Override public void dequeue(DequeueContext dequeue) { if (size()>0) { + // essentially what we do is for every child replace the repeater with the child in + // dequeue container stack and run the dequeue on the child. we also take care to reset + // the state of the dequeue context after we process every child. + Bookmark bookmark=dequeue.save(); for (Component child:this) {