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;
018
019import java.lang.ref.WeakReference;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.wicket.Component;
028import org.apache.wicket.MarkupContainer;
029import org.apache.wicket.behavior.Behavior;
030import org.apache.wicket.markup.parser.XmlTag;
031import org.apache.wicket.markup.parser.XmlTag.TagType;
032import org.apache.wicket.markup.parser.filter.HtmlHandler;
033import org.apache.wicket.request.Response;
034import org.apache.wicket.util.lang.Args;
035import org.apache.wicket.util.lang.Generics;
036import org.apache.wicket.util.string.StringValue;
037import org.apache.wicket.util.string.Strings;
038import org.apache.wicket.util.value.IValueMap;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042
043/**
044 * A subclass of MarkupElement which represents a "significant" markup tag, such as a component open
045 * tag. Insignificant markup tags (those which are merely concerned with markup formatting
046 * operations and do not denote components or component nesting) are coalesced into instances of
047 * RawMarkup (also a subclass of MarkupElement).
048 * 
049 * @author Jonathan Locke
050 */
051public class ComponentTag extends MarkupElement
052{
053        /**
054         * Factory that creates component during markup root container's initialization. These
055         * components get queued, which allows other components to be dequeued under these auto
056         * components.
057         * 
058         * @author igor
059         */
060        public interface IAutoComponentFactory
061        {
062                /**
063                 * Creates a new instance of auto component to be queued
064                 *
065                 * @param container
066                 *                The component that will become a parent of the newly created auto component
067                 * @param tag
068                 *                The markup element for the newly created auto component
069                 */
070                Component newComponent(MarkupContainer container, ComponentTag tag);
071        }
072
073
074        /** Log. */
075        private static final Logger log = LoggerFactory.getLogger(ComponentTag.class);
076
077        /** True if a href attribute is available and autolinking is on */
078        private final static int AUTOLINK = 0x0001;
079
080        /** True, if attributes have been modified or added */
081        private final static int MODIFIED = 0x0002;
082
083        /** If true, then the MarkupParser will ignore (remove) it. Temporary working variable */
084        private final static int IGNORE = 0x0004;
085
086        /** If true, then the tag contain an automatically created wicket id */
087        private final static int AUTO_COMPONENT = 0x0008;
088
089        /** Some HTML tags are allow to have no close tag, e.g. 'br' */
090        private final static int NO_CLOSE_TAG = 0x0010;
091
092        /** Render the tag as RawMarkup even if no Component can be found */
093        public final static int RENDER_RAW = 0x0020;
094        
095        /** If true, the current tag contains a child or a descendant with the "wicket:id" attribute */
096        public final static int CONTAINS_WICKET_ID = 0x0040;
097
098        /** If close tag, than reference to the corresponding open tag */
099        private ComponentTag openTag;
100
101        /** The underlying xml tag */
102        protected final XmlTag xmlTag;
103
104        /** Boolean flags. See above */
105        private int flags = 0;
106
107        /**
108         * By default this is equal to the wicket:id="xxx" attribute value, but may be provided e.g. for
109         * auto-tags
110         */
111        private String id;
112
113        /**
114         * In case of inherited markup, the base and the extended markups are merged and the information
115         * about the tags origin is lost. In some cases like wicket:head and wicket:link this
116         * information however is required.
117         */
118        private WeakReference<Class<? extends Component>> markupClassRef = null;
119
120        /** added behaviors */
121        private List<Behavior> behaviors;
122
123        /** Filters and Handlers may add their own attributes to the tag */
124        private Map<String, Object> userData;
125
126        private IAutoComponentFactory autoComponentFactory;
127
128        /**
129         * Automatically create a XmlTag, assign the name and the type, and construct a ComponentTag
130         * based on this XmlTag.
131         * 
132         * @param name
133         *            The name of html tag
134         * @param type
135         *            The type of tag
136         */
137        public ComponentTag(final String name, final XmlTag.TagType type)
138        {
139                final XmlTag tag = new XmlTag();
140                tag.setName(name);
141                tag.setType(type);
142                xmlTag = tag;
143        }
144
145        /**
146         * Construct.
147         * 
148         * @param tag
149         *            The underlying xml tag
150         */
151        public ComponentTag(final XmlTag tag)
152        {
153                super();
154                xmlTag = tag;
155        }
156
157        /**
158         * Constructor
159         * 
160         * @param tag
161         *            The ComponentTag tag which this wicket tag is based upon.
162         */
163        public ComponentTag(final ComponentTag tag)
164        {
165                this(tag.getXmlTag());
166                tag.copyPropertiesTo(this);
167        }
168
169        /**
170         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
171         * 
172         * @param flag
173         *            The flag to set
174         * @param set
175         *            True to turn the flag on, false to turn it off
176         */
177        public final void setFlag(final int flag, final boolean set)
178        {
179                if (set)
180                {
181                        flags |= flag;
182                }
183                else
184                {
185                        flags &= ~flag;
186                }
187        }
188
189        /**
190         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
191         * 
192         * @param flag
193         *            The flag to test
194         * @return True if the flag is set
195         */
196        public final boolean getFlag(final int flag)
197        {
198                return (flags & flag) != 0;
199        }
200
201        /**
202         * Adds a behavior to this component tag.
203         * 
204         * @param behavior
205         */
206        public final void addBehavior(final Behavior behavior)
207        {
208                Args.notNull(behavior, "behavior");
209
210                if (behaviors == null)
211                {
212                        behaviors = Generics.newArrayList();
213                }
214                behaviors.add(behavior);
215        }
216
217        /**
218         * @return true if this tag has any behaviors added, false otherwise
219         */
220        public final boolean hasBehaviors()
221        {
222                return behaviors != null;
223        }
224
225        /**
226         * @return read only iterator over added behaviors
227         */
228        // TODO change to return unmodifiable list which will never be null. See Component.getBehavior
229        public final Iterator<? extends Behavior> getBehaviors()
230        {
231                if (behaviors == null)
232                {
233                        List<Behavior> lst = Collections.emptyList();
234                        return lst.iterator();
235                }
236
237                return Collections.unmodifiableCollection(behaviors).iterator();
238        }
239
240        /**
241         * Gets whether this tag closes the provided open tag.
242         * 
243         * @param open
244         *            The open tag
245         * @return True if this tag closes the given open tag
246         */
247        @Override
248        public final boolean closes(final MarkupElement open)
249        {
250                if (open instanceof ComponentTag)
251                {
252                        return (openTag == open) || getXmlTag().closes(((ComponentTag)open).getXmlTag());
253                }
254
255                return false;
256        }
257
258        /**
259         * If autolink is set to true, href attributes will automatically be converted into Wicket
260         * bookmarkable URLs.
261         * 
262         * @param autolink
263         *            enable/disable automatic href conversion
264         */
265        public final void enableAutolink(final boolean autolink)
266        {
267                setFlag(AUTOLINK, autolink);
268        }
269
270        /**
271         * @see org.apache.wicket.markup.parser.XmlTag#getAttributes()
272         * @return The tag#s attributes
273         */
274        public final IValueMap getAttributes()
275        {
276                return xmlTag.getAttributes();
277        }
278
279        /**
280         * A convenient method. The same as getAttributes().getString(name)
281         * 
282         * @param name
283         * @return The attributes value
284         */
285        public final String getAttribute(String name)
286        {
287                return xmlTag.getAttributes().getString(name);
288        }
289
290        /**
291         * Get the tag's component id
292         * 
293         * @return The component id attribute of this tag
294         */
295        public final String getId()
296        {
297                return id;
298        }
299
300        /**
301         * Gets the length of the tag in characters.
302         * 
303         * @return The tag's length
304         */
305        public final int getLength()
306        {
307                return xmlTag.getLength();
308        }
309
310        /**
311         * @return The tag's name
312         */
313        public final String getName()
314        {
315                return xmlTag.getName();
316        }
317
318        /**
319         * @return The tag's namespace
320         */
321        public final String getNamespace()
322        {
323                return xmlTag.getNamespace();
324        }
325
326        /**
327         * If set, return the corresponding open tag (ComponentTag).
328         * 
329         * @return The corresponding open tag
330         */
331        public final ComponentTag getOpenTag()
332        {
333                return openTag;
334        }
335
336        /**
337         * @see org.apache.wicket.markup.parser.XmlTag#getPos()
338         * @return Tag location (index in input string)
339         */
340        public final int getPos()
341        {
342                return xmlTag.getPos();
343        }
344
345        /**
346         * @return the tag type (OPEN, CLOSE or OPEN_CLOSE).
347         */
348        public final TagType getType()
349        {
350                return xmlTag.getType();
351        }
352
353        /**
354         * True if autolink is enabled and the tag contains a href attribute.
355         * 
356         * @return True, if the href contained should automatically be converted
357         */
358        public final boolean isAutolinkEnabled()
359        {
360                return getFlag(AUTOLINK);
361        }
362
363        /**
364         * @see org.apache.wicket.markup.parser.XmlTag#isClose()
365         * @return True if this tag is a close tag
366         */
367        public final boolean isClose()
368        {
369                return xmlTag.isClose();
370        }
371
372        /**
373         * @see org.apache.wicket.markup.parser.XmlTag#isOpen()
374         * @return True if this tag is an open tag
375         */
376        public final boolean isOpen()
377        {
378                return xmlTag.isOpen();
379        }
380
381        /**
382         * @param id
383         *            Required component id
384         * @return True if this tag is an open tag with the given component name
385         * @see org.apache.wicket.markup.parser.XmlTag#isOpen()
386         */
387        public final boolean isOpen(String id)
388        {
389                return xmlTag.isOpen() && this.id.equals(id);
390        }
391
392        /**
393         * @see org.apache.wicket.markup.parser.XmlTag#isOpenClose()
394         * @return True if this tag is an open and a close tag
395         */
396        public final boolean isOpenClose()
397        {
398                return xmlTag.isOpenClose();
399        }
400
401        /**
402         * @param id
403         *            Required component id
404         * @return True if this tag is an openclose tag with the given component id
405         * @see org.apache.wicket.markup.parser.XmlTag#isOpenClose()
406         */
407        public final boolean isOpenClose(String id)
408        {
409                return xmlTag.isOpenClose() && this.id.equals(id);
410        }
411
412        /**
413         * Makes this tag object immutable by making the attribute map unmodifiable. Immutable tags
414         * cannot be made mutable again. They can only be copied into new mutable tag objects.
415         */
416        public final void makeImmutable()
417        {
418                xmlTag.makeImmutable();
419        }
420
421        /**
422         * Gets this tag if it is already mutable, or a mutable copy of this tag if it is immutable.
423         * 
424         * @return This tag if it is already mutable, or a mutable copy of this tag if it is immutable.
425         */
426        public ComponentTag mutable()
427        {
428                if (xmlTag.isMutable())
429                {
430                        return this;
431                }
432                else
433                {
434                        ComponentTag tag = new ComponentTag(xmlTag.mutable());
435                        copyPropertiesTo(tag);
436                        return tag;
437                }
438        }
439
440        /**
441         * Copies all internal properties from this tag to <code>dest</code>. This is basically cloning
442         * without instance creation.
443         * 
444         * @param dest
445         *            tag whose properties will be set
446         */
447        void copyPropertiesTo(final ComponentTag dest)
448        {
449                dest.id = id;
450                dest.flags = flags;
451                dest.autoComponentFactory = autoComponentFactory;
452
453                if (markupClassRef != null)
454                {
455                        dest.setMarkupClass(markupClassRef.get());
456                }
457                if (behaviors != null)
458                {
459                        dest.behaviors = new ArrayList<>(behaviors);
460                }
461                if (userData != null)
462                {
463                        dest.userData = new HashMap<>(userData);
464                }
465        }
466
467        /**
468         * @see org.apache.wicket.markup.parser.XmlTag#put(String, boolean)
469         * @param key
470         *            The key
471         * @param value
472         *            The value
473         */
474        public final void put(final String key, final boolean value)
475        {
476                xmlTag.put(key, value);
477                setModified(true);
478        }
479
480        /**
481         * @see org.apache.wicket.markup.parser.XmlTag#put(String, int)
482         * @param key
483         *            The key
484         * @param value
485         *            The value
486         */
487        public final void put(final String key, final int value)
488        {
489                xmlTag.put(key, value);
490                setModified(true);
491        }
492
493        /**
494         * @see org.apache.wicket.markup.parser.XmlTag#put(String, CharSequence)
495         * @param key
496         *            The key
497         * @param value
498         *            The value
499         */
500        public final void put(String key, CharSequence value)
501        {
502                checkIdAttribute(key);
503                putInternal(key, value);
504        }
505
506        /**
507         * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL IT
508         * 
509         * @see org.apache.wicket.markup.parser.XmlTag#put(String, CharSequence)
510         * @param key
511         *            The key
512         * @param value
513         *            The value
514         */
515        public final void putInternal(String key, CharSequence value)
516        {
517                xmlTag.put(key, value);
518                setModified(true);
519        }
520
521        /**
522         * @param key
523         */
524        private void checkIdAttribute(String key)
525        {
526                if ("id".equalsIgnoreCase(key))
527                {
528                        log.warn("Please use component.setMarkupId(String) to change the tag's 'id' attribute.");
529                }
530        }
531
532        /**
533         * Appends specified {@code value} to the attribute
534         * 
535         * @param key
536         *            The key
537         * @param value
538         *            The value
539         * @param separator
540         *            The separator used to append the value
541         */
542        public final void append(String key, CharSequence value, String separator)
543        {
544                String current = getAttribute(key);
545                if (Strings.isEmpty(current))
546                {
547                        xmlTag.put(key, value);
548                }
549                else
550                {
551                        xmlTag.put(key, current + separator + value);
552                }
553
554                setModified(true);
555        }
556
557        /**
558         * @see org.apache.wicket.markup.parser.XmlTag#put(String, StringValue)
559         * @param key
560         *            The key
561         * @param value
562         *            The value
563         */
564        public final void put(String key, StringValue value)
565        {
566                xmlTag.put(key, value);
567                setModified(true);
568        }
569
570        /**
571         * @see org.apache.wicket.markup.parser.XmlTag#putAll(Map)
572         * @param map
573         *            a key/value map
574         */
575        public final void putAll(final Map<String, Object> map)
576        {
577                xmlTag.putAll(map);
578                setModified(true);
579        }
580
581        /**
582         * @see org.apache.wicket.markup.parser.XmlTag#remove(String)
583         * @param key
584         *            The key to remove
585         */
586        public final void remove(String key)
587        {
588                xmlTag.remove(key);
589                setModified(true);
590        }
591
592        /**
593         * Gets whether this tag does not require a closing tag.
594         * 
595         * @return True if this tag does not require a closing tag
596         */
597        public final boolean requiresCloseTag()
598        {
599                if (getNamespace() == null)
600                {
601                        return HtmlHandler.requiresCloseTag(getName());
602                }
603                else
604                {
605                        return HtmlHandler.requiresCloseTag(getNamespace() + ":" + getName());
606                }
607        }
608
609        /**
610         * Set the component's id. The value is usually taken from the tag's id attribute, e.g.
611         * wicket:id="componentId".
612         * 
613         * @param id
614         *            The component's id assigned to the tag.
615         */
616        public final void setId(final String id)
617        {
618                this.id = id;
619        }
620
621        /**
622         * @see org.apache.wicket.markup.parser.XmlTag#setName(String)
623         * @param name
624         *            New tag name
625         */
626        public final void setName(String name)
627        {
628                xmlTag.setName(name);
629        }
630
631        /**
632         * @see org.apache.wicket.markup.parser.XmlTag#setNamespace(String)
633         * @param namespace
634         *            New tag name namespace
635         */
636        public final void setNamespace(String namespace)
637        {
638                xmlTag.setNamespace(namespace);
639        }
640
641        /**
642         * Assuming this is a close tag, assign it's corresponding open tag.
643         * 
644         * @param tag
645         *            the open-tag
646         * @throws RuntimeException
647         *             if 'this' is not a close tag
648         */
649        public final void setOpenTag(final ComponentTag tag)
650        {
651                openTag = tag;
652                getXmlTag().setOpenTag(tag.getXmlTag());
653        }
654
655        /**
656         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
657         * 
658         * @param type
659         *            The new type
660         */
661        public final void setType(final TagType type)
662        {
663                if (type != xmlTag.getType())
664                {
665                        xmlTag.setType(type);
666                        setModified(true);
667                }
668        }
669
670        /**
671         * Writes the synthetic close tag for this tag to the response
672         * 
673         * @param response
674         *            The response to write to
675         */
676        public final void writeSyntheticCloseTag(Response response)
677        {
678                response.write("</");
679                if (getNamespace() != null)
680                {
681                        response.write(getNamespace());
682                        response.write(":");
683                }
684                response.write(getName());
685                response.write(">");
686        }
687
688        /**
689         * @see org.apache.wicket.markup.MarkupElement#toCharSequence()
690         */
691        @Override
692        public CharSequence toCharSequence()
693        {
694                return xmlTag.toCharSequence();
695        }
696
697        /**
698         * Converts this object to a string representation.
699         * 
700         * @return String version of this object
701         */
702        @Override
703        public final String toString()
704        {
705                return toCharSequence().toString();
706        }
707
708        /**
709         * Write the tag to the response
710         * 
711         * @param response
712         *            The response to write to
713         * @param stripWicketAttributes
714         *            if true, wicket:id are removed from output
715         * @param namespace
716         *            Wicket's namespace to use
717         */
718        public final void writeOutput(final Response response, final boolean stripWicketAttributes,
719                final String namespace)
720        {
721                response.write("<");
722
723                if (getType() == TagType.CLOSE)
724                {
725                        response.write("/");
726                }
727
728                if (getNamespace() != null)
729                {
730                        response.write(getNamespace());
731                        response.write(":");
732                }
733
734                response.write(getName());
735
736                String namespacePrefix = null;
737                if (stripWicketAttributes == true)
738                {
739                        namespacePrefix = namespace + ":";
740                }
741
742                if (getAttributes().size() > 0)
743                {
744                        for (String key : getAttributes().keySet())
745                        {
746                                if (key == null)
747                                {
748                                        continue;
749                                }
750
751                                if ((namespacePrefix == null) || (key.startsWith(namespacePrefix) == false))
752                                {
753                                        response.write(" ");
754                                        response.write(key);
755                                        CharSequence value = getAttribute(key);
756
757                                        // attributes without values are possible, e.g.' disabled'
758                                        if (value != null)
759                                        {
760                                                response.write("=\"");
761                                                value = Strings.escapeMarkup(value);
762                                                response.write(value);
763                                                response.write("\"");
764                                        }
765                                }
766                        }
767                }
768
769                if (getType() == TagType.OPEN_CLOSE)
770                {
771                        response.write("/");
772                }
773
774                response.write(">");
775        }
776
777        /**
778         * Converts this object to a string representation including useful information for debugging
779         * 
780         * @return String version of this object
781         */
782        @Override
783        public final String toUserDebugString()
784        {
785                return xmlTag.toUserDebugString();
786        }
787
788        /**
789         * @return Returns the underlying xml tag.
790         */
791        public final XmlTag getXmlTag()
792        {
793                return xmlTag;
794        }
795
796        /**
797         * Manually mark the ComponentTag being modified. Flagging the tag being modified does not
798         * happen automatically.
799         * 
800         * @param modified
801         */
802        public final void setModified(final boolean modified)
803        {
804                setFlag(MODIFIED, modified);
805        }
806
807        /**
808         * 
809         * @return True, if the component tag has been marked modified
810         */
811        final boolean isModified()
812        {
813                return getFlag(MODIFIED);
814        }
815
816        /**
817         * 
818         * @return True if the HTML tag (e.g. br) has no close tag
819         */
820        public boolean hasNoCloseTag()
821        {
822                return getFlag(NO_CLOSE_TAG);
823        }
824
825        /**
826         * True if the HTML tag (e.g. br) has no close tag
827         * 
828         * @param hasNoCloseTag
829         */
830        public void setHasNoCloseTag(boolean hasNoCloseTag)
831        {
832                setFlag(NO_CLOSE_TAG, hasNoCloseTag);
833        }
834        
835        /**
836         * Sets the flag to indicate if the current tag contains a child 
837         * or a descendant with the "wicket:id" attribute. 
838         *
839         * @param containsWicketId
840         */
841        public void setContainsWicketId(boolean containsWicketId)
842        {
843                setFlag(CONTAINS_WICKET_ID, containsWicketId);
844        }
845
846        /**
847         * Says if the current tag contains a child or a descendant with the "wicket:id" attribute.
848         * @return true if the current tag contains a child or a descendant with the "wicket:id" attribute.
849         */
850    public boolean containsWicketId()
851    {
852        return getFlag(CONTAINS_WICKET_ID);
853    }
854    
855        /**
856         * In case of inherited markup, the base and the extended markups are merged and the information
857         * about the tags origin is lost. In some cases like wicket:head and wicket:link this
858         * information however is required.
859         * 
860         * @return wicketHeaderClass
861         */
862        public Class<? extends Component> getMarkupClass()
863        {
864                return (markupClassRef == null ? null : markupClassRef.get());
865        }
866
867        /**
868         * Set the class of wicket component which contains the wicket:head tag.
869         * 
870         * @param <C>
871         * 
872         * @param wicketHeaderClass
873         *            wicketHeaderClass
874         */
875        public <C extends Component> void setMarkupClass(Class<C> wicketHeaderClass)
876        {
877                if (wicketHeaderClass == null)
878                {
879                        markupClassRef = null;
880                }
881                else
882                {
883                        markupClassRef = new WeakReference<Class<? extends Component>>(wicketHeaderClass);
884                }
885        }
886
887        /**
888         * @see org.apache.wicket.markup.MarkupElement#equalTo(org.apache.wicket.markup.MarkupElement)
889         */
890        @Override
891        public boolean equalTo(final MarkupElement element)
892        {
893                if (element instanceof ComponentTag)
894                {
895                        final ComponentTag that = (ComponentTag)element;
896                        return getXmlTag().equalTo(that.getXmlTag());
897                }
898                return false;
899        }
900
901        /**
902         * Gets ignore.
903         * 
904         * @return If true than MarkupParser will remove it from the markup
905         */
906        public boolean isIgnore()
907        {
908                return getFlag(IGNORE);
909        }
910
911        /**
912         * Sets ignore.
913         * 
914         * @param ignore
915         *            If true than MarkupParser will remove it from the markup
916         */
917        public void setIgnore(boolean ignore)
918        {
919                setFlag(IGNORE, ignore);
920        }
921
922        /**
923         * @return True, if wicket:id has been automatically created (internal component)
924         */
925        public boolean isAutoComponentTag()
926        {
927                return getFlag(AUTO_COMPONENT);
928        }
929
930        /**
931         * @param auto
932         *            True, if wicket:id has been automatically created (internal component)
933         */
934        public void setAutoComponentTag(boolean auto)
935        {
936                setFlag(AUTO_COMPONENT, auto);
937        }
938
939        /**
940         * Gets userData.
941         * 
942         * @param key
943         *            The key to store and retrieve the value
944         * @return userData
945         */
946        public Object getUserData(final String key)
947        {
948                if (userData == null)
949                {
950                        return null;
951                }
952
953                return userData.get(key);
954        }
955
956        /**
957         * Sets userData.
958         * 
959         * @param key
960         *            The key to store and retrieve the value
961         * @param value
962         *            The user specific value to store
963         */
964        public void setUserData(final String key, final Object value)
965        {
966                if (userData == null)
967                {
968                        userData = new HashMap<>();
969                }
970                userData.put(key, value);
971        }
972
973        /**
974         * For subclasses to override. Gets called just before a Component gets rendered. It is
975         * guaranteed that the markupStream is set on the Component and determineVisibility is not yet
976         * called.
977         * 
978         * @param component
979         *            The component that is about to be rendered
980         * @param markupStream
981         *            The current markup stream
982         */
983        public void onBeforeRender(final Component component, final MarkupStream markupStream)
984        {
985        }
986
987        public IAutoComponentFactory getAutoComponentFactory()
988        {
989                return autoComponentFactory;
990        }
991
992        public void setAutoComponentFactory(IAutoComponentFactory autoComponentFactory)
993        {
994                this.autoComponentFactory = autoComponentFactory;
995        }
996
997
998}