001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.panel;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.markup.ComponentTag;
021import org.apache.wicket.markup.MarkupException;
022import org.apache.wicket.markup.MarkupStream;
023
024/**
025 * The Panel components markup sourcing strategy.
026 * <p>
027 * The strategy supports two modes on how to handle the body markup. A typical Panel will ignore the
028 * body markup and replace it with the associated markup. The body markup is allowed to have raw
029 * markup only and no Wicket components. But e.g. a Border component will associate the body markup
030 * with a Body component which renders the markup including all any number of child Components.
031 * 
032 * @author Juergen Donnerstag
033 */
034public class PanelMarkupSourcingStrategy extends AssociatedMarkupSourcingStrategy
035{
036        private static final PanelMarkupSourcingStrategy PANEL_INSTANCE = new PanelMarkupSourcingStrategy(
037                false);
038        private static final PanelMarkupSourcingStrategy BORDER_INSTANCE = new PanelMarkupSourcingStrategy(
039                true);
040
041        // False for Panel and true for Border components.
042        private final boolean allowWicketComponentsInBodyMarkup;
043
044        /**
045         * @param allowWicketComponentsInBodyMarkup
046         *            {@code false} for Panel and {@code true} for Border components. If Panel then the
047         *            body markup should only contain raw markup, which is ignored (removed), but no
048         *            Wicket Component. With Border components, the body markup will be associated with
049         *            the Body Component.
050         * 
051         * @return A singleton of the strategy
052         */
053        public static PanelMarkupSourcingStrategy get(final boolean allowWicketComponentsInBodyMarkup)
054        {
055                return allowWicketComponentsInBodyMarkup ? BORDER_INSTANCE : PANEL_INSTANCE;
056        }
057
058        /**
059         * Constructor.
060         * 
061         * @param wicketTagName
062         *            The tag name for <code>&lt;wicket:'name' ..&gt;</code>. Please note that any such
063         *            tag must have been registered via
064         *            <code>WicketTagIdentifier.registerWellKnownTagName("name");</code>
065         * @param allowWicketComponentsInBodyMarkup
066         *            {@code false} for Panel and {@code true} for Border components. If Panel then the
067         *            body markup should only contain raw markup, which is ignored (removed), but no
068         *            Wicket Component. With Border components, the body markup will be associated with
069         *            the Body Component.
070         */
071        public PanelMarkupSourcingStrategy(final String wicketTagName,
072                final boolean allowWicketComponentsInBodyMarkup)
073        {
074                super(wicketTagName);
075
076                this.allowWicketComponentsInBodyMarkup = allowWicketComponentsInBodyMarkup;
077        }
078
079        /**
080         * Constructor.
081         * 
082         * @param allowWicketComponentsInBodyMarkup
083         *            {@code false} for Panel and {@code true} for Border components. If Panel then the
084         *            body markup should only contain raw markup, which is ignored (removed), but no
085         *            Wicket Component. With Border components, the body markup will be associated with
086         *            the Body Component.
087         */
088        public PanelMarkupSourcingStrategy(final boolean allowWicketComponentsInBodyMarkup)
089        {
090                this(Panel.PANEL, allowWicketComponentsInBodyMarkup);
091        }
092
093        /**
094         * Skip the panel's body markup which is expected to contain raw markup only (no wicket
095         * components) and which will be ignored / removed. It'll be replaced with the content of the
096         * associated markup file.
097         */
098        @Override
099        public void onComponentTagBody(final Component component, final MarkupStream markupStream,
100                final ComponentTag openTag)
101        {
102                if (allowWicketComponentsInBodyMarkup)
103                {
104                        // Skip the body markup. Will be picked up by the Body component.
105                        markupStream.skipToMatchingCloseTag(openTag);
106                }
107                else
108                {
109                        // Skip the components body. Like with Panels or Fragments, it'll be replaced with the
110                        // associated markup
111                        if (markupStream.getPreviousTag().isOpen())
112                        {
113                                markupStream.skipRawMarkup();
114                                if (markupStream.get().closes(openTag) == false)
115                                {
116                                        StringBuilder msg = new StringBuilder();
117
118                                        msg.append("Close tag not found for tag: ")
119                                                .append(openTag.toString())
120                                                .append(". For ")
121                                                .append(component.getClass().getSimpleName())
122                                                .append(" Components only raw markup is allow in between the tags but not ")
123                                                .append("other Wicket Component. Component: ")
124                                                .append(component.toString());
125
126                                        throw new MarkupException(markupStream, msg.toString());
127                                }
128                        }
129                }
130
131                renderAssociatedMarkup(component);
132        }
133}