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.digester.Digester;
037: import org.apache.commons.digester.Rule;
038: import org.apache.commons.digester.RuleSetBase;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041: import org.xml.sax.Attributes;
042: import org.xml.sax.helpers.AttributesImpl;
043:
044: import javax.servlet.jsp.tagext.Tag;
045: import java.util.Enumeration;
046: import java.util.Hashtable;
047:
048: /**
049: * This class provides a ruleset for the Apache Digester, that supports parsing
050: * of a JSFX page into a tree of JSP tag processing classes. This tree is then
051: * used in the parser to mimick the JSP lifecycle in order to produce the JSF
052: * component tree.
053: *
054: * @author Steve Maryka
055: */
056: public class ComponentRuleSet extends RuleSetBase {
057:
058: /**
059: * The tag to tag processing class map.
060: */
061: private TagToComponentMap map;
062: private String viewTagClass;
063: private String ruleNamespace;
064: private static String TAG_NEST = "*/";
065: private static Class setPropertiesRuleClass = null;
066: private static boolean isJSF12 = false;
067:
068: private static final Log log = LogFactory
069: .getLog(ComponentRuleSet.class);
070:
071: static {
072: try {
073: setPropertiesRuleClass = Class
074: .forName("org.apache.commons.digester.SetPropertiesRule");
075: //Test for JSF 1.2
076: Class.forName("javax.faces.webapp.UIComponentELTag");
077: setPropertiesRuleClass = Class
078: .forName("com.icesoft.faces.webapp.parser.ELSetPropertiesRule");
079: if (log.isDebugEnabled()) {
080: log.debug("ICEfaces using JSF 1.2 JSP tags.");
081: }
082: isJSF12 = true;
083: } catch (Throwable t) {
084: //many different throwables besides ClassNotFoundException
085: if (log.isDebugEnabled()) {
086: log
087: .debug("No JSF 1.2 classes found. Running in JSF 1.1 environment");
088: }
089: }
090: }
091:
092: /**
093: * Constructor
094: *
095: * @param mp The map from tags to tag processing classes
096: * @param namespaceURI
097: */
098: public ComponentRuleSet(TagToComponentMap mp, String namespaceURI) {
099: super ();
100: map = mp;
101: ruleNamespace = namespaceURI;
102: }
103:
104: /**
105: * Detect JSF 1.2 for special cases
106: *
107: * @return true if JSF 1.2 is being used
108: */
109: public static boolean isJSF12() {
110: return isJSF12;
111: }
112:
113: /**
114: * Adds rules for each tag in the map.
115: *
116: * @param digester The digester to which rules are added.
117: */
118: public void addRuleInstances(Digester digester) {
119:
120: Hashtable table = map.getTagToComponentMap();
121: Enumeration keys = table.keys();
122: TagRule tagRule = new TagRule();
123: XhtmlTagRule xhtmlTagRule = new XhtmlTagRule();
124:
125: digester.setRuleNamespaceURI(ruleNamespace);
126:
127: while (keys.hasMoreElements()) {
128: String key = (String) keys.nextElement();
129: String tagType = (String) table.get(key);
130:
131: if (!key.equals("view")) {
132: /* We don't want to create view object during parsing
133: as it will be created as the root of the component tree
134: prior to parsing */
135:
136: // Want to create tag object regardless of type;
137: digester.addObjectCreate(TAG_NEST + key, tagType);
138:
139: if (tagType.equals(XhtmlTag.class.getName())) {
140: // Rules for Xhtml Tags;
141: digester.addObjectCreate(TAG_NEST + key,
142: "com.icesoft.faces.webapp.parser.TagWire");
143: digester.addRule(TAG_NEST + key, xhtmlTagRule);
144: } else {
145: // Rules for JSF Tags;
146: try {
147: digester.addRule(TAG_NEST + key,
148: (Rule) setPropertiesRuleClass
149: .newInstance());
150: } catch (Exception e) {
151: if (log.isDebugEnabled()) {
152: log.debug(e.getMessage(), e);
153: }
154: }
155: digester.addObjectCreate(TAG_NEST + key,
156: "com.icesoft.faces.webapp.parser.TagWire");
157: digester.addRule(TAG_NEST + key, tagRule);
158: }
159: } else {
160: // Capture the view tag class;
161: ((JsfJspDigester) digester)
162: .setViewTagClassName(tagType);
163: viewTagClass = tagType;
164: }
165: }
166: }
167:
168: public String getViewTagClass() {
169: return viewTagClass;
170: }
171: }
172:
173: /**
174: * Contains base functionality for all rules.
175: */
176: abstract class BaseRule extends Rule {
177:
178: /**
179: * Constructor
180: */
181: public BaseRule() {
182: super ();
183: }
184:
185: /**
186: * Body processing for the rule. The body text for this tag gets trimmed
187: * up, and an outputTextTag is created to hold the body text.
188: *
189: * @param namespace Namespace provided by digester.
190: * @param name Name provided by digester.
191: * @param text The body text.
192: */
193: public void body(String namespace, String name, String text) {
194:
195: String bodyText = text.trim();
196: if (bodyText.length() > 0) {
197: TagWire wire = (TagWire) digester.peek();
198: Tag child = (Tag) digester.peek(1);
199: IceOutputTextTag bodyTextTag = new IceOutputTextTag();
200: TagWire newWire = new TagWire();
201: bodyTextTag.setValue(bodyText);
202: wireUpTheTag((Tag) bodyTextTag, child, newWire, wire);
203: }
204: }
205:
206: /**
207: * Because Tag processing classes themselves don't support a tree structure,
208: * a wiring class is used to hold the tree together. This member wires up
209: * the tree appropriately.
210: *
211: * @param child The child Tag
212: * @param parent The parent Tag
213: * @param childWire The child Wire
214: * @param parentWire The parent Wire
215: */
216: protected void wireUpTheTag(Tag child, Tag parent,
217: TagWire childWire, TagWire parentWire) {
218:
219: child.setParent(parent);
220: childWire.setTag(child);
221: parentWire.addChild(childWire);
222: }
223:
224: /**
225: * This function peeks into the digester to see if there is body text
226: * assiciated with the parent. If there is, an Output Text tag is created
227: * to hold that body text.
228: *
229: * @param parent The parent Tag.
230: * @param parentWire The parent Wire.
231: */
232: protected void dealWithPreceedingBodyText(Tag parent,
233: TagWire parentWire) {
234: /* This function peeks into the digester to see if there is body text
235: associated with the parent. If there is, an OutputText component is
236: created to hold that body text */
237:
238: // In this version we create an IceOutputTextTag and wire it in;
239: String bodyText = ((JsfJspDigester) digester)
240: .stealParentBodyText();
241: if (bodyText != null) {
242: IceOutputTextTag bodyTextTag = new IceOutputTextTag();
243: TagWire wire = new TagWire();
244: bodyTextTag.setValue(bodyText);
245: wireUpTheTag((Tag) bodyTextTag, parent, wire, parentWire);
246: }
247: }
248: }
249:
250: /**
251: * This class provides the rule for processing JSF tags. The tag attributes are
252: * saved and the tag is wired into the tag processing tree.
253: */
254: final class TagRule extends BaseRule {
255:
256: /**
257: * Constructor.
258: */
259: public TagRule() {
260: super ();
261: }
262:
263: /**
264: * The begin processing for the rule. Saves attributes, deals with parent
265: * body text, and wires up tag processing tree.
266: *
267: * @param attributes Attributes for the Tag.
268: * @throws Exception No exception is thrown.
269: */
270: public void begin(Attributes attributes) throws Exception {
271:
272: // Get all the important bits off the digester stack;
273: TagWire wire = (TagWire) digester.peek();
274: Tag child = (Tag) digester.peek(1);
275: TagWire parentWire = (TagWire) digester.peek(2);
276: Tag parent = (Tag) digester.peek(3);
277:
278: Attributes cloned = clone(attributes);
279: wire.setAttributes(cloned);
280:
281: // Deal with preceeding body text;
282: dealWithPreceedingBodyText(parent, parentWire);
283:
284: // Wire up the tree;
285: wireUpTheTag(child, parent, wire, parentWire);
286: }
287:
288: /**
289: * Create clone of attributes.
290: *
291: * @param attributes Attributes to clone.
292: * @return Cloned attributes.
293: */
294: private Attributes clone(Attributes attributes) {
295: Attributes clone = new AttributesImpl(attributes);
296: for (int i = 0; i < clone.getLength(); i++) {
297: String name = attributes.getQName(i);
298: String value = attributes.getValue(name);
299: ((AttributesImpl) clone).setLocalName(i, name);
300: ((AttributesImpl) clone).setValue(i, value);
301: }
302: return clone;
303: }
304: }
305:
306: /**
307: * A rule for processing Xhtml Tags. Need to save the tag name for future
308: * processing.
309: */
310: final class XhtmlTagRule extends BaseRule {
311:
312: /**
313: * Constructor.
314: */
315: public XhtmlTagRule() {
316: super ();
317: }
318:
319: /**
320: * Begin processing for the rule. Saves attributes, saves tag name, deals
321: * with parent's preceeding body text, and wires up the tag processing
322: * tree.
323: *
324: * @param attributes Attributes for the tag.
325: * @throws Exception No exception is thrown.
326: */
327: public void begin(Attributes attributes) throws Exception {
328:
329: // Get all the important bits off the digester stack;
330: TagWire wire = (TagWire) digester.peek();
331: XhtmlTag child = (XhtmlTag) digester.peek(1);
332: TagWire parentWire = (TagWire) digester.peek(2);
333: Tag parent = (Tag) digester.peek(3);
334:
335: // Save attributes;
336: child
337: .setAttributes((Attributes) (new AttributesImpl(
338: attributes)));
339:
340: // Save the tag;
341: child.setTagName(new String(digester.getCurrentElementName()));
342:
343: // Deal with preceeding body text;
344: dealWithPreceedingBodyText(parent, parentWire);
345:
346: // Wire up the tree;
347: wireUpTheTag((Tag) child, parent, wire, parentWire);
348: }
349: }
|