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