001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.webapp.parser;
035:
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038:
039: import javax.faces.FacesException;
040: import javax.faces.component.UIComponent;
041: import javax.faces.component.UIViewRoot;
042: import javax.faces.context.FacesContext;
043: import javax.faces.webapp.UIComponentBodyTag;
044: import javax.faces.webapp.UIComponentTag;
045: import javax.servlet.jsp.JspException;
046: import javax.servlet.jsp.JspWriter;
047: import javax.servlet.jsp.PageContext;
048: import javax.servlet.jsp.tagext.Tag;
049: import java.io.IOException;
050: import java.io.InputStream;
051: import java.io.PrintWriter;
052: import java.io.Reader;
053: import java.util.HashSet;
054: import java.util.Iterator;
055: import java.util.Set;
056:
057: /**
058: * This is the JSFX parser. It digests a JSFX file into a tag processing tree,
059: * and then executes the JSP tag processing lifecycle over that tree to produce
060: * the JSF component tree.
061: * <p/>
062: * A parser is initialized with a list of tags that it processes. This list of
063: * tags comes from a serialized version of a TagToComponentMap, which is passed
064: * in as an application initialization parameter. The parser should be created
065: * and initialized by the ViewHandler implementation for the application. Once
066: * it is initialized, the parser can parse a jsf jsp and initialize the
067: * component tree for the page. The parser relies on the apache Digester, and a
068: * set of rules created in the ComponentRuleSet class.
069: *
070: * @author Steve Maryka
071: */
072: public class Parser {
073: private static final Log log = LogFactory.getLog(Parser.class);
074: private JsfJspDigester digester;
075:
076: public Parser(InputStream fis) throws IOException {
077: // Create digester and add rules;
078: digester = new JsfJspDigester();
079: digester.setNamespaceAware(true);
080: digester.setValidating(false);
081: digester.setUseContextClassLoader(false);
082: digester.setClassLoader(this .getClass().getClassLoader());
083:
084: try {
085: TagToComponentMap map = TagToComponentMap.loadFrom(fis);
086: digester.addRuleSet(new ComponentRuleSet(map, ""));
087: } catch (ClassNotFoundException e) {
088: throw new IOException(e.getMessage());
089: }
090: }
091:
092: /**
093: * The main parsing logic. Creates a Digester to parse page into a tag
094: * processing tree, and then executes the JSP lifecycle across that tree.
095: * The end result is a JSF component rooted with a UIViewRoot component.
096: *
097: * @param page The Reader for the page.
098: * @param context
099: * @throws java.io.IOException If stream IO fails.
100: * @throws org.xml.sax.SAXException If digester encounters invalid XML.
101: */
102: public void parse(Reader page, FacesContext context)
103: throws java.io.IOException, org.xml.sax.SAXException {
104: // Need a mock pageContext
105: StubPageContext pageContext = new StubPageContext();
106: //Get rid of old view root
107: StubHttpServletResponse response = new StubHttpServletResponse();
108: pageContext.initialize(null, null, response, null, false, 1024,
109: false);
110: Set componentIds = new HashSet();
111:
112: //placeholder tag and wire
113: XhtmlTag rootTag = new XhtmlTag();
114: rootTag.setTagName("ICEtag");
115: rootTag.setParent(null);
116: TagWire rootWire = new TagWire();
117: rootWire.setTag(rootTag);
118:
119: synchronized (this ) {
120: digester.clear();
121: digester.push(rootTag);
122: digester.push(rootWire);
123: digester.parse(page);
124: }
125:
126: try {
127: String viewTagClassName = digester.getViewTagClassName();
128: if (null == viewTagClassName)
129: throw new IllegalStateException(
130: "ICEfaces parser unable to determine JSF implementation ViewTag class.");
131: Tag viewTag = (Tag) Class.forName(viewTagClassName)
132: .newInstance();
133: viewTag.setParent(null);
134: rootWire.setTag(viewTag);
135:
136: executeJspLifecycle(rootWire, pageContext, context,
137: componentIds);
138: pageContext.removeAttribute(
139: "javax.faces.webapp.COMPONENT_TAG_STACK",
140: PageContext.REQUEST_SCOPE);
141: } catch (Exception e) {
142: log.error("Failed to execute JSP lifecycle.", e);
143: throw new FacesException(
144: "Failed to execute JSP lifecycle.", e);
145: }
146: }
147:
148: /**
149: * This member mimicks the JSP tag processing lifecyle across the tag
150: * processing tree to produce the JSF component tree.
151: *
152: * @param wire The tag's wire
153: * @param pageContext The page context
154: * @param facesContext The faces context
155: * @param componentIds
156: * @throws JspException
157: */
158: public void executeJspLifecycle(TagWire wire,
159: PageContext pageContext, FacesContext facesContext,
160: Set componentIds) throws JspException {
161: // Start of lifecycle;
162: boolean processingViewTag = false;
163: Tag tag = wire.getTag();
164: tag.setPageContext(pageContext);
165: // Start tag processing;
166: tag.doStartTag();
167:
168: if (tag instanceof UIComponentTag) {
169: UIComponentTag compTag = (UIComponentTag) tag;
170: UIComponent myComponent = compTag.getComponentInstance();
171:
172: if (myComponent != null) {
173: if (myComponent instanceof UIViewRoot) {
174: myComponent.setId("_view");
175: processingViewTag = true;
176: }
177:
178: String componentId = myComponent
179: .getClientId(facesContext);
180: if (componentIds.contains(componentId))
181: throw new IllegalStateException(
182: "Duplicate component ID : " + componentId);
183: componentIds.add(componentId);
184: }
185: }
186:
187: // Now process tag children;
188: Iterator children = wire.getChildren().iterator();
189: while (children.hasNext()) {
190: TagWire childWire = (TagWire) children.next();
191: executeJspLifecycle(childWire, pageContext, facesContext,
192: componentIds);
193: }
194: //Do tag body processing. This is not full-fledged body processing. It only calls the doAfterBody() member
195: if (!processingViewTag && tag instanceof UIComponentBodyTag) {
196: UIComponentBodyTag bodyTag = (UIComponentBodyTag) tag;
197: if (bodyTag.getBodyContent() == null) {
198: //body content of custom tag should not be null, so create one in here to satisfy the
199: //checking in jsf 1.0 impl.
200: JspWriter jspWriter = new JspWriterImpl(
201: new PrintWriter(System.out));
202: BodyContentImpl imp = new BodyContentImpl(jspWriter);
203: bodyTag.setBodyContent(imp);
204: }
205: bodyTag.doAfterBody();
206: }
207: // Do end tag processing;
208: tag.doEndTag();
209: }
210: }
|