001: /*
002: * Created on Feb 18, 2005
003: */
004: package com.sun.portal.wireless.htmlconversion;
005:
006: import java.util.Stack;
007: import java.util.HashMap;
008:
009: import org.w3c.dom.Document;
010: import org.w3c.dom.Element;
011:
012: import com.sun.portal.wireless.htmlconversion.servlet.URLTranscoder;
013: import com.sun.portal.wireless.htmlconversion.processors.TextTag;
014:
015: /**
016: * Maintains state of input parsing, and output document.
017: *
018: * @author ashwin.mathew@sun.com
019: */
020: public class ParserState {
021:
022: // The output AML document
023: private Document output;
024:
025: // Indicates whether or not the root element has been set
026: // on output
027: private boolean isDocRootSet = false;
028:
029: // Container tag context on output AML document
030: // Used to figure out which element to appendChild() to
031: // Note that tags which cannot have children are *never*
032: // present on this stack.
033: private Stack outputContainerTagContext = new Stack();
034:
035: // Acts like a stack of TextTags, used for maintaining
036: // text formatting context on input HTML document
037: private TextTag textTagContext;
038:
039: // Text buffer, as the name indicates
040: // All consumers of this data *must* call
041: // clearText() after consuming the buffer,
042: // so that the same data is not rendered twice.
043: private StringBuffer text = new StringBuffer();
044:
045: // The current output element
046: private Element currentOutputTag;
047:
048: // Maintains arbitrary bits of state for
049: // different tag processors
050: private HashMap processorState = new HashMap();
051:
052: // Indicates that tag processing should be bypassed
053: // Used by tag processors that need to handle their
054: // own bodies without any other tag processors being
055: // invoked
056: private boolean bypassTagProcessing = false;
057:
058: // The HTML end tag on which tag processing should
059: // be resumed.
060: private String tagProcessingResumeTag = null;
061:
062: private LayoutManager layoutManager;
063:
064: private boolean isInAmlForm = false;
065:
066: private URLTranscoder encoder;
067:
068: public ParserState(Document output, URLTranscoder encoder) {
069: this .output = output;
070: this .encoder = encoder;
071: this .textTagContext = TextTag.NOSTYLE;
072: this .layoutManager = new LayoutManager(this );
073: }
074:
075: /**
076: * Returns the output Document object.
077: */
078: public Document getOutputDocument() {
079: return output;
080: }
081:
082: /**
083: * Sets the new TextTag context.
084: *
085: * @param context
086: */
087: public void setTextTagContext(TextTag context) {
088: TextTag nextContext = new TextTag(textTagContext, context);
089:
090: textTagContext = nextContext;
091: }
092:
093: /**
094: * Returns the current TextTag context.
095: * @return
096: */
097: public TextTag getTextTagContext() {
098: return textTagContext;
099: }
100:
101: /**
102: * Rolls back to the last TextTag context.
103: */
104: public void rollbackTextTagContext() {
105: TextTag lastContext = textTagContext.getParent();
106:
107: textTagContext = lastContext;
108: }
109:
110: /**
111: * Appends the supplied char[] to the text buffer.
112: *
113: * @param data
114: */
115: public void appendText(char[] data, int start, int length) {
116: text.append(data, start, length);
117: }
118:
119: /**
120: * Returns the contents of the text buffer.
121: *
122: * @return
123: */
124: public String getText() {
125: return text.toString();
126: }
127:
128: /**
129: * Clears the text buffer.
130: */
131: public void clearText() {
132: text.setLength(0);
133: }
134:
135: /**
136: * Indicates whether or not there is text in the text buffer.
137: */
138: public boolean isTextAvailable() {
139: return (text.length() > 0);
140: }
141:
142: /**
143: * Returns a new Element build using the contained output Document. Required
144: * by tag processors.
145: *
146: * @param name The tag name for the element.
147: * @return The required Element.
148: */
149: public Element newElement(String name) {
150: return output.createElement(name);
151: }
152:
153: /**
154: * Switches the output container tag context to the specified element.
155: *
156: * @param tag
157: */
158: public void setOutputContainerTag(Element tag) {
159: outputContainerTagContext.push(tag);
160:
161: if (isDocRootSet == false) {
162: output.appendChild(tag);
163: isDocRootSet = true;
164: }
165: }
166:
167: /**
168: * Returns the current output container tag context.
169: *
170: * @return
171: */
172: public Element getOutputContainerTag() {
173: if (outputContainerTagContext.isEmpty()) {
174: return null;
175: } else {
176: return (Element) outputContainerTagContext.peek();
177: }
178: }
179:
180: /**
181: * Rolls back the output container tag context to the last tag context.
182: */
183: public void rollbackOutputContainerTag() {
184: Element tag = (Element) outputContainerTagContext.pop();
185: }
186:
187: /**
188: * Returns the current output element.
189: */
190: public Element getCurrentOutputTag() {
191: return currentOutputTag;
192: }
193:
194: /**
195: * Sets the current output element.
196: * Note that this must be called *only* if a corresponding
197: * HTML end tag will cause a rollback to occur i.e., if an output
198: * tag is being created independent of the HTML tag stream,
199: * it must not be set using this method.
200: */
201: public void setCurrentOutputTag(Element nextOutputTag) {
202: currentOutputTag = nextOutputTag;
203: }
204:
205: /**
206: * Rolls back the current output tag to the
207: * current output container tag context.
208: */
209: public void rollbackCurrentOutputTag() {
210: currentOutputTag = getOutputContainerTag();
211: }
212:
213: /**
214: * Sets the specified name/value pair as a processor attribute.
215: *
216: * @param name
217: * @param value
218: */
219: public void setProcessorAttribute(String name, Object value) {
220: processorState.put(name, value);
221: }
222:
223: /**
224: * Returns the value of the named processor attribute.
225: *
226: * @param name
227: * @return
228: */
229: public Object getProcessorAttribute(String name) {
230: return processorState.get(name);
231: }
232:
233: /**
234: * Indicates that all subsequent tag processing
235: * should be bypassed until the HTML end tag specified
236: * by resumeOnTag is encountered. This is typically
237: * the endTag for the tag processor that invokes
238: * this method.
239: */
240: public void setBypassTagProcessing(String resumeOnTag) {
241: bypassTagProcessing = true;
242: tagProcessingResumeTag = resumeOnTag;
243: }
244:
245: /**
246: * Indicates whether or not tag processing
247: * is being bypassed.
248: *
249: * @return
250: */
251: public boolean isBypassTagProcessing() {
252: return bypassTagProcessing;
253: }
254:
255: /**
256: * Resumes normal tag processing.
257: */
258: public void resumeTagProcessing() {
259: bypassTagProcessing = false;
260: tagProcessingResumeTag = null;
261: }
262:
263: /**
264: * Returns the HTML end tag on which normal
265: * tag processing should be resumed.
266: *
267: * @return
268: */
269: public String getTagProcessingResumeTag() {
270: return tagProcessingResumeTag;
271: }
272:
273: /**
274: * Returns the LayoutManager.
275: *
276: * @return
277: */
278: public LayoutManager getLayoutManager() {
279: return layoutManager;
280: }
281:
282: /**
283: * Indicates whether or not the current output
284: * contained within a form.
285: *
286: * @return
287: */
288: public boolean isInAmlForm() {
289: return isInAmlForm;
290: }
291:
292: /**
293: * Called on start of an AmlForm.
294: */
295: public void startedAmlForm() {
296: isInAmlForm = true;
297: }
298:
299: /**
300: * Called on end of an AmlForm
301: */
302: public void endedAmlForm() {
303: isInAmlForm = false;
304: }
305:
306: public URLTranscoder getEncoder() {
307: return encoder;
308: }
309:
310: }
|