Home | History | Annotate | Download | only in serializer
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: ElemContext.java 468654 2006-10-28 07:09:23Z minchau $
     20  */
     21 package org.apache.xml.serializer;
     22 
     23 /**
     24  * This class is a stack frame that consists of
     25  * information about the element currently being processed
     26  * by a serializer. Consider this example:
     27  * <pre>
     28  *   <A>
     29  *     <B1>
     30  *     </B1>
     31  *     <B2>
     32  *     </B2>
     33  *   <A>
     34  * </pre>
     35  *
     36  * A stack frame will be pushed for "A" at depth 1,
     37  * then another one for "B1" at depth 2.
     38  * Then "B1" stackframe is popped.  When the stack frame for "B2" is
     39  * pushed, this implementation re-uses the old stack fram object used
     40  * by "B1" to be efficient at not creating too many of these object.
     41  *
     42  * This is by no means a public class, and neither are its fields or methods,
     43  * they are all helper fields for a serializer.
     44  *
     45  * The purpose of this class is to be more consistent with pushing information
     46  * when a new element is being serialized and more quickly restoring the old
     47  * information about the parent element with a simple pop() when the
     48  * child element is done.  Previously there was some redundant and error-prone
     49  * calculations going on to retore information.
     50  *
     51  * @xsl.usage internal
     52  */
     53 final class ElemContext
     54 {
     55     // Fields that form the context of the element
     56 
     57     /**
     58      * The nesting depth of the element inside other elements.
     59      */
     60     final int m_currentElemDepth;
     61 
     62     /** HTML field, the element description of the HTML element */
     63     ElemDesc m_elementDesc = null;
     64 
     65     /**
     66      * The local name of the element.
     67      */
     68     String m_elementLocalName = null;
     69 
     70     /**
     71      * The fully qualified name of the element (with prefix, if any).
     72      */
     73     String m_elementName = null;
     74 
     75     /**
     76      * The URI of the element.
     77      * If this value is null it means that the URI is not yet determined
     78      * for the element. Valid values are the empty string "", meaning
     79      * that it is in no namespace, or a string of non-zero length.
     80      */
     81     String m_elementURI = null;
     82 
     83     /** If the element is in the cdata-section-names list
     84      * then the value is true. If it is true the text children of the element
     85      * should be output in CDATA section blocks.
     86      */
     87     boolean m_isCdataSection;
     88 
     89     /** True if the current element has output escaping disabled.
     90      * This is true for SCRIPT and STYLE elements.
     91      */
     92     boolean m_isRaw = false;
     93 
     94     /** The next element "stack frame". This value will only be
     95      * set once as deeper stack frames are not deleted when popped off,
     96      * but are rather re-used when a push is required.
     97      *
     98      * This makes for very fast pushing and popping of stack frames
     99      * because very few stack frame objects are ever created, they are
    100      * mostly re-used.  This re-use saves object creation but it also means
    101      * that connections between the frames via m_next and m_prev
    102      * never changes either. Just the contents of the frames change
    103      * as they are re-used. Only the reference to the current stack frame, which
    104      * is held by the serializer is changed via a quick pop() or push().
    105      */
    106     private ElemContext m_next;
    107 
    108     /** The previous element "stack frame". */
    109     final ElemContext m_prev;
    110 
    111     /**
    112      * Set to true when a start tag is started, or open, but not all the
    113      * attributes or namespace information is yet collected.
    114      */
    115     boolean m_startTagOpen = false;
    116 
    117     /**
    118      * Constructor to create the root of the element contexts.
    119      *
    120      */
    121     ElemContext()
    122     {
    123         // this assignment means can never pop this context off
    124         m_prev = this;
    125         // depth 0 because it doesn't correspond to any element
    126         m_currentElemDepth = 0;
    127     }
    128 
    129     /**
    130      * Constructor to create the "stack frame" for a given element depth.
    131      *
    132      * This implementation will re-use the context at each depth. If
    133      * a documents deepest element depth is N then there will be (N+1)
    134      * such objects created, no more than that.
    135      *
    136      * @param previous The "stack frame" corresponding to the new
    137      * elements parent element.
    138      */
    139     private ElemContext(final ElemContext previous)
    140     {
    141         m_prev = previous;
    142         m_currentElemDepth = previous.m_currentElemDepth + 1;
    143     }
    144 
    145     /**
    146      * Pop the current "stack frame".
    147      * @return Returns the parent "stack frame" of the one popped.
    148      */
    149     final ElemContext pop()
    150     {
    151         /* a very simple pop.  No clean up is done of the deeper
    152          * stack frame.  All deeper stack frames are still attached
    153          * but dormant, just waiting to be re-used.
    154          */
    155         return this.m_prev;
    156     }
    157 
    158     /**
    159      * This method pushes an element "stack frame"
    160      * but with no initialization of values in that frame.
    161      * This method is used for optimization purposes, like when pushing
    162      * a stack frame for an HTML "IMG" tag which has no children and
    163      * the stack frame will almost immediately be popped.
    164      */
    165     final ElemContext push()
    166     {
    167         ElemContext frame = this.m_next;
    168         if (frame == null)
    169         {
    170             /* We have never been at this depth yet, and there is no
    171              * stack frame to re-use, so we now make a new one.
    172              */
    173             frame = new ElemContext(this);
    174             this.m_next = frame;
    175         }
    176         /*
    177          * We shouldn't need to set this true because we should just
    178          * be pushing a dummy stack frame that will be instantly popped.
    179          * Yet we need to be ready in case this element does have
    180          * unexpected children.
    181          */
    182         frame.m_startTagOpen = true;
    183         return frame;
    184     }
    185 
    186     /**
    187      * Push an element context on the stack. This context keeps track of
    188      * information gathered about the element.
    189      * @param uri The URI for the namespace for the element name,
    190      * can be null if it is not yet known.
    191      * @param localName The local name of the element (no prefix),
    192      * can be null.
    193      * @param qName The qualified name (with prefix, if any)
    194      * of the element, this parameter is required.
    195      */
    196     final ElemContext push(
    197         final String uri,
    198         final String localName,
    199         final String qName)
    200     {
    201         ElemContext frame = this.m_next;
    202         if (frame == null)
    203         {
    204             /* We have never been at this depth yet, and there is no
    205              * stack frame to re-use, so we now make a new one.
    206              */
    207             frame = new ElemContext(this);
    208             this.m_next = frame;
    209         }
    210 
    211         // Initialize, or reset values in the new or re-used stack frame.
    212         frame.m_elementName = qName;
    213         frame.m_elementLocalName = localName;
    214         frame.m_elementURI = uri;
    215         frame.m_isCdataSection = false;
    216         frame.m_startTagOpen = true;
    217 
    218         // is_Raw is already set in the HTML startElement() method
    219         // frame.m_isRaw = false;
    220         return frame;
    221     }
    222 }
    223