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.loader;
018
019import java.io.IOException;
020
021import org.apache.wicket.MarkupContainer;
022import org.apache.wicket.markup.IMarkupFragment;
023import org.apache.wicket.markup.Markup;
024import org.apache.wicket.markup.MarkupFactory;
025import org.apache.wicket.markup.MarkupNotFoundException;
026import org.apache.wicket.markup.MarkupResourceStream;
027import org.apache.wicket.markup.MergedMarkup;
028import org.apache.wicket.markup.TagUtils;
029import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
030import org.apache.wicket.core.util.resource.locator.ResourceStreamLocator;
031
032/**
033 * Merge the 2+ markups involved in markup inheritance. From a users perspective there is only one
034 * markup associated with the component, the merged one.
035 * 
036 * @author Juergen Donnerstag
037 */
038public class InheritedMarkupMarkupLoader implements IMarkupLoader
039{
040        /**
041         * Constructor.
042         */
043        public InheritedMarkupMarkupLoader()
044        {
045        }
046
047        /**
048         * Load the markup from the resource stream with the base MarkupLoader provided, than check if
049         * markup inheritance must be applied. If yes, than load the base markup and merge them.
050         */
051        @Override
052        public final Markup loadMarkup(final MarkupContainer container,
053                final MarkupResourceStream markupResourceStream, final IMarkupLoader baseLoader,
054                final boolean enforceReload) throws IOException, ResourceStreamNotFoundException
055        {
056                // read and parse the markup
057                Markup markup = baseLoader.loadMarkup(container, markupResourceStream, null, enforceReload);
058
059                // Check if markup contains <wicket:extend> which tells us that
060                // we need to read the inherited markup as well.
061                int extendIndex = requiresBaseMarkup(markup);
062                if (extendIndex == -1)
063                {
064                        return markup;
065                }
066
067                // Load the base markup
068                final Markup baseMarkup = getBaseMarkup(container, markup, enforceReload);
069                if ((baseMarkup == null) || (baseMarkup == Markup.NO_MARKUP))
070                {
071                        throw new MarkupNotFoundException(
072                                "Base markup of inherited markup not found. Component class: " +
073                                        markup.getMarkupResourceStream()
074                                                .getContainerInfo()
075                                                .getContainerClass()
076                                                .getName() +
077                                        ". Enable debug messages for " + ResourceStreamLocator.class.getName() +
078                                        " to get a list of all filenames tried.");
079                }
080
081                // Merge base and derived markup
082                return new MergedMarkup(markup, baseMarkup, extendIndex);
083        }
084
085        /**
086         * Load the base markup
087         * 
088         * @param container
089         * @param markup
090         * @param enforceReload
091         * @return the base markup
092         */
093        private Markup getBaseMarkup(final MarkupContainer container, final Markup markup,
094                final boolean enforceReload)
095        {
096                final Class<?> location = markup.getMarkupResourceStream().getMarkupClass().getSuperclass();
097
098                // get the base markup
099                return MarkupFactory.get().getMarkup(container, location, enforceReload);
100        }
101
102        /**
103         * Check if markup contains &lt;wicket:extend&gt; which tells us that we need to read the
104         * inherited markup as well. &lt;wicket:extend&gt; MUST BE the first wicket tag in the markup.
105         * Skip raw markup
106         * 
107         * @param markup
108         * @return == 0, if no wicket:extend was found
109         */
110        private int requiresBaseMarkup(final IMarkupFragment markup)
111        {
112                for (int i = 0; i < markup.size(); i++)
113                {
114                        if (TagUtils.isExtendTag(markup, i))
115                        {
116                                // Ok, inheritance is on and we must get the
117                                // inherited markup as well.
118                                return i;
119                        }
120                }
121                return -1;
122        }
123}