0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package com.sun.rave.web.ui.util;
0042:
0043: import com.sun.rave.web.ui.component.util.descriptors.LayoutElement;
0044: import com.sun.rave.web.ui.component.util.descriptors.LayoutComponent;
0045:
0046: import java.lang.reflect.Field;
0047: import java.lang.reflect.Modifier;
0048: import java.util.ArrayList;
0049: import java.util.Iterator;
0050: import java.util.HashMap;
0051: import java.util.List;
0052: import java.util.Map;
0053: import java.util.ResourceBundle;
0054: import java.util.Stack;
0055:
0056: import javax.faces.component.NamingContainer;
0057: import javax.faces.component.UIComponent;
0058: import javax.faces.component.UIViewRoot;
0059: import javax.faces.context.FacesContext;
0060: import javax.faces.event.ActionEvent;
0061:
0062: /**
0063: * <p> VariableResolver is used to parse expressions of the format.</p>
0064: *
0065: * <p> <dd>$<type$gt;{<key>}</dd></p>
0066: *
0067: * <p> <type> refers to a registerd {@link VariableResolver.DataSource},
0068: * custom {@link VariableResolver.DataSource}s can be registered via:
0069: * {@link #setDataSource(String key,
0070: * VariableResolver.DataSource dataSource)}.
0071: * However, there are many built-in {@link VariableResolver.DataSource}
0072: * types that are pre-registered.</p>
0073: *
0074: * <p> Below are the pre-registered types: </p>
0075: *
0076: * <ul><li>{@link #ATTRIBUTE} -- {@link AttributeDataSource}</li>
0077: * <li>{@link #BOOLEAN} -- {@link BooleanDataSource}</li>
0078: * <li>{@link #BROWSER} -- {@link BrowserDataSource}</li>
0079: * <li>{@link #CONSTANT} -- {@link ConstantDataSource}</li>
0080: * <li>{@link #ESCAPE} -- {@link EscapeDataSource}</li>
0081: * <li>{@link #HAS_FACET} -- {@link HasFacetDataSource}</li>
0082: * <li>{@link #HAS_PROPERTY} -- {@link HasPropertyDataSource}</li>
0083: * <li>{@link #INT} -- {@link IntDataSource}</li>
0084: * <li>{@link #METHOD_BINDING} -- {@link MethodBindingDataSource}</li>
0085: * <li>{@link #PROPERTY} -- {@link PropertyDataSource}</li>
0086: * <li>{@link #REQUEST_PARAMETER} --
0087: * {@link RequestParameterDataSource}</li>
0088: * <li>{@link #RESOURCE} -- {@link ResourceBundleDataSource}</li>
0089: * <li>{@link #SESSION} -- {@link SessionDataSource}</li>
0090: * <li>{@link #STYLE} -- {@link StyleDataSource}</li>
0091: * <li>{@link #THEME_JS} -- {@link ThemeJavaScriptDataSource}</li>
0092: * <li>{@link #THIS} -- {@link ThisDataSource}</li></ul>
0093: *
0094: * @author Ken Paulsen (ken.paulsen@sun.com)
0095: */
0096: public class VariableResolver {
0097:
0098: /**
0099: * <p> This method will substitute variables into the given String, or
0100: * return the variable if the substitution is the whole String. This
0101: * method looks for the LAST occurance of startToken in the given
0102: * String. It then searches from that location (if found) to the
0103: * first occurance of typeDelim. The value inbetween is used as the
0104: * type of substitution to perform (i.e. request attribute, session,
0105: * etc.). It next looks for the next occurance of endToken. The
0106: * value inbetween is used as the key passed to the
0107: * {@link VariableResolver.DataSource} specified by the type. The
0108: * String value from the {@link VariableResolver.DataSource} replaces
0109: * the portion of the String from the startToken to the endToken. If
0110: * this is the entire String, the Object is returned instead of the
0111: * String value. This process is repeated until no more
0112: * substitutions are * needed.</p>
0113: *
0114: * <p> This algorithm will accomodate nested variables (e.g. "${A{$x}}").
0115: * It also allows the replacement value itself to contain variables.
0116: * Care should be taken to ensure that the replacement String included
0117: * does not directly or indirectly refer to itself -- this will cause
0118: * an infinite loop.</p>
0119: *
0120: * <p> There is one special case where the string to be evaluated begins
0121: * with the startToken and ends with the endToken. In this case,
0122: * string substitution is NOT performed. Instead the value of the
0123: * request attribute is returned.</p>
0124: *
0125: * @param ctx The FacesContext
0126: * @param desc The closest LayoutElement to this string
0127: * @param component The assoicated UIComponent
0128: * @param string The string to be evaluated.
0129: * @param startToken Marks the beginning "$"
0130: * @param typeDelim Marks separation of type/variable "{"
0131: * @param endToken Marks the end of the variable "}"
0132: *
0133: * @return The new string with substitutions, or the specified request
0134: * attribute value.
0135: */
0136: public static Object resolveVariables(FacesContext ctx,
0137: LayoutElement desc, UIComponent component, String string,
0138: String startToken, String typeDelim, String endToken) {
0139:
0140: int stringLen = string.length();
0141: int delimIndex;
0142: int endIndex;
0143: int parenSemi;
0144: int startTokenLen = startToken.length();
0145: int delimLen = typeDelim.length();
0146: int endTokenLen = endToken.length();
0147: boolean expressionIsWholeString = false;
0148: char firstEndChar = SUB_END.charAt(0);
0149: char firstDelimChar = SUB_TYPE_DELIM.charAt(0);
0150: char currChar;
0151: String type;
0152: Object variable;
0153:
0154: for (int startIndex = string.lastIndexOf(startToken); startIndex != -1; startIndex = string
0155: .lastIndexOf(startToken, startIndex - 1)) {
0156:
0157: // Find first typeDelim
0158: delimIndex = string.indexOf(typeDelim, startIndex
0159: + startTokenLen);
0160: if (delimIndex == -1) {
0161: continue;
0162: }
0163:
0164: // Next find the end token
0165: parenSemi = 0;
0166: endIndex = -1;
0167: // Iterate through the string looking for the matching end
0168: for (int curr = delimIndex + delimLen; curr < stringLen;) {
0169: // Get the next char...
0170: currChar = string.charAt(curr);
0171: if ((currChar == firstDelimChar)
0172: && typeDelim.equals(string.substring(curr, curr
0173: + delimLen))) {
0174: // Found the start of another... inc the semi
0175: parenSemi++;
0176: curr += delimLen;
0177: continue;
0178: }
0179: if ((currChar == firstEndChar)
0180: && endToken.equals(string.substring(curr, curr
0181: + endTokenLen))) {
0182: parenSemi--;
0183: if (parenSemi < 0) {
0184: // Found the right one!
0185: endIndex = curr;
0186: break;
0187: }
0188: // Found one, but this isn't the right one
0189: curr += endTokenLen;
0190: continue;
0191: }
0192: curr++;
0193: }
0194: if (endIndex == -1) {
0195: // We didn't find a matching end...
0196: continue;
0197: }
0198:
0199: /*
0200: // Next find end token
0201: endIndex = string.indexOf(endToken, delimIndex+delimLen);
0202: matchingIndex = string.lastIndexOf(typeDelim, endIndex);
0203: while ((endIndex != -1) && (matchingIndex != delimIndex)) {
0204: // We found a endToken, but not the matching one...keep looking
0205: endIndex = string.indexOf(endToken, endIndex+endTokenLen);
0206: matchingIndex = string.lastIndexOf(typeDelim,
0207: matchingIndex-delimLen);
0208: }
0209: if ((endIndex == -1) || (matchingIndex == -1)) {
0210: continue;
0211: }
0212: */
0213:
0214: // Handle special case where string starts with startToken and ends
0215: // with endToken (and no replacements inbetween). This is special
0216: // because we don't want to convert the attribute to a string, we
0217: // want to return it (this allows Object types).
0218: if ((startIndex == 0)
0219: && (endIndex == string.lastIndexOf(endToken))
0220: && (string.endsWith(endToken))) {
0221: // This is the special case...
0222: expressionIsWholeString = true;
0223: }
0224:
0225: // Pull off the type...
0226: type = string.substring(startIndex + startTokenLen,
0227: delimIndex);
0228: DataSource ds = (DataSource) dataSourceMap.get(type);
0229: if (ds == null) {
0230: throw new IllegalArgumentException("Invalid type '"
0231: + type + "' in attribute value: '" + string
0232: + "'.");
0233: }
0234:
0235: // Pull off the variable...
0236: variable = string
0237: .substring(delimIndex + delimLen, endIndex);
0238:
0239: // Get the value...
0240: variable = ds.getValue(ctx, desc, component,
0241: (String) variable);
0242: if (expressionIsWholeString) {
0243: return variable;
0244: }
0245:
0246: // Make new string
0247: string = string.substring(0, startIndex)
0248: + // Before replacement
0249: ((variable == null) ? "" : variable.toString())
0250: + string.substring(endIndex + endTokenLen); // After
0251: stringLen = string.length();
0252: }
0253:
0254: // Return the string
0255: return string;
0256: }
0257:
0258: /**
0259: * This method replaces the ${..} variables with their values. It will
0260: * only do this for Strings and List's that contain Strings.
0261: *
0262: * @param desc The <code>LayoutElement</code> descriptor
0263: * @param component The <code>UIComponent</code>
0264: * @param value The value to resolve
0265: *
0266: * @return The result
0267: */
0268: public static Object resolveVariables(LayoutElement desc,
0269: UIComponent component, Object value) {
0270: if (value == null) {
0271: return null;
0272: }
0273: return VariableResolver.resolveVariables(FacesContext
0274: .getCurrentInstance(), desc, component, value);
0275: }
0276:
0277: /**
0278: * This method replaces the ${..} variables with their attribute values.
0279: * It will only do this for Strings and List's that contain Strings.
0280: *
0281: * @param ctx The <code>FacesContext</code>
0282: * @param desc The <code>LayoutElement</code> descriptor
0283: * @param component The <code>UIComponent</code>
0284: * @param value The value to resolve
0285: *
0286: * @return The result
0287: */
0288: public static Object resolveVariables(FacesContext ctx,
0289: LayoutElement desc, UIComponent component, Object value) {
0290: if (value == null) {
0291: return null;
0292: }
0293: if (value instanceof String) {
0294: value = VariableResolver.resolveVariables(ctx, desc,
0295: component, (String) value,
0296: VariableResolver.SUB_START,
0297: VariableResolver.SUB_TYPE_DELIM,
0298: VariableResolver.SUB_END);
0299: } else if (value instanceof List) {
0300: // Create a new List b/c invalid to change shared List
0301: List list = ((List) value);
0302: int size = list.size();
0303: List newList = new ArrayList(size);
0304: Iterator it = list.iterator();
0305: while (it.hasNext()) {
0306: newList.add(VariableResolver.resolveVariables(ctx,
0307: desc, component, it.next()));
0308: }
0309: return newList;
0310: }
0311: return value;
0312: }
0313:
0314: /**
0315: * <p> This method looks up the requested
0316: * {@link VariableResolver.DataSource} by the given key.
0317: *
0318: * @param key The key identifying the desired
0319: * {@link VariableResolver.DataSource}
0320: *
0321: * @return The requested {@link VariableResolver.DataSource}
0322: */
0323: public static VariableResolver.DataSource getDataSource(String key) {
0324: return (VariableResolver.DataSource) dataSourceMap.get(key);
0325: }
0326:
0327: /**
0328: * <p> This method sets the given {@link VariableResolver.DataSource} to
0329: * be used for $[type]{...} when key matches type.</p>
0330: *
0331: * @param key The key identifying the
0332: * {@link VariableResolver.DataSource}
0333: * @param dataSource The {@link VariableResolver.DataSource}
0334: */
0335: public static void setDataSource(String key,
0336: VariableResolver.DataSource dataSource) {
0337: dataSourceMap.put(key, dataSource);
0338: }
0339:
0340: /**
0341: * <p> This interface defines a String substitution data source. This
0342: * is used to retrieve values when a $<type>{<data>} is
0343: * encountered within a parameter value.</p>
0344: *
0345: * <p> Implementations of this interface may register themselves
0346: * statically to extend the capabilities of the ${} substitution
0347: * mechanism.</p>
0348: */
0349: public interface DataSource {
0350: /**
0351: * <p> This method should return the resolved value based on the
0352: * given key and contextual information.</p>
0353: *
0354: * @param ctx The <code>FacesContext</code>
0355: * @param desc The <code>LayoutElement</code>
0356: * @param component The <code>UIComponent</code>
0357: * @param key The key used to obtain information from this
0358: * <code>DataSource</code>.
0359: *
0360: * @return The value resolved from key.
0361: */
0362: Object getValue(FacesContext ctx, LayoutElement desc,
0363: UIComponent component, String key);
0364: }
0365:
0366: /**
0367: * <p> This {@link VariableResolver.DataSource} provides access to
0368: * HttpRequest attributes. It uses the data portion of the
0369: * substitution String as a key to the HttpRequest attribute Map.</p>
0370: */
0371: public static class AttributeDataSource implements DataSource {
0372: /**
0373: * <p> See class JavaDoc.</p>
0374: *
0375: * @param ctx The <code>FacesContext</code>
0376: * @param desc The <code>LayoutElement</code>
0377: * @param component The <code>UIComponent</code>
0378: * @param key The key used to obtain information from this
0379: * <code>DataSource</code>.
0380: *
0381: * @return The value resolved from key.
0382: */
0383: public Object getValue(FacesContext ctx, LayoutElement desc,
0384: UIComponent component, String key) {
0385: return ctx.getExternalContext().getRequestMap().get(key);
0386: }
0387: }
0388:
0389: /**
0390: * <p> This {@link VariableResolver.DataSource} provides access to
0391: * PageSession attributes. It uses the data portion of the
0392: * substitution String as a key to the PageSession attribute Map.</p>
0393: public static class PageSessionDataSource implements DataSource {
0394: /**
0395: * <p> See class JavaDoc.</p>
0396: *
0397: * @param ctx The <code>FacesContext</code>
0398: * @param desc The <code>LayoutElement</code>
0399: * @param component The <code>UIComponent</code>
0400: * @param key The key used to obtain information from this
0401: * <code>DataSource</code>.
0402: *
0403: * @return The value resolved from key.
0404: public Object getValue(FacesContext ctx, LayoutElement desc,
0405: UIComponent component, String key) {
0406: while (desc.getParent() != null) {
0407: desc = desc.getParent();
0408: }
0409: return ((ViewBean)desc.getView(ctx)).getPageSessionAttribute(key);
0410: }
0411: }
0412: */
0413:
0414: /**
0415: * <p> This {@link VariableResolver.DataSource} provides access to
0416: * HttpRequest Parameters. It uses the data portion of the
0417: * substitution String as a key to the HttpRequest Parameter Map.</p>
0418: */
0419: public static class RequestParameterDataSource implements
0420: DataSource {
0421: /**
0422: * <p> See class JavaDoc.</p>
0423: *
0424: * @param ctx The <code>FacesContext</code>
0425: * @param desc The <code>LayoutElement</code>
0426: * @param component The <code>UIComponent</code>
0427: * @param key The key used to obtain information from this
0428: * <code>DataSource</code>.
0429: *
0430: * @return The value resolved from key.
0431: */
0432: public Object getValue(FacesContext ctx, LayoutElement desc,
0433: UIComponent component, String key) {
0434: return ctx.getExternalContext().getRequestParameterMap()
0435: .get(key);
0436: }
0437: }
0438:
0439: /**
0440: * <p> This {@link VariableResolver.DataSource} provides access to
0441: * UIComponent Properties. It uses the data portion of the
0442: * substitution String as a key to the UIComponent's properties via
0443: * the attribute Map. If the property is null, it will attempt to
0444: * look at the parent's properties.</p>
0445: */
0446: public static class PropertyDataSource implements DataSource {
0447: /**
0448: * <p> See class JavaDoc.</p>
0449: *
0450: * @param ctx The <code>FacesContext</code>
0451: * @param desc The <code>LayoutElement</code>
0452: * @param component The <code>UIComponent</code>
0453: * @param key The key used to obtain information from this
0454: * <code>DataSource</code>.
0455: *
0456: * @return The value resolved from key.
0457: */
0458: public Object getValue(FacesContext ctx, LayoutElement desc,
0459: UIComponent component, String key) {
0460:
0461: // Check to see if we should walk up the tree or not
0462: int idx = key.indexOf(',');
0463: boolean walk = false;
0464: if (idx > 0) {
0465: walk = Boolean.valueOf(key.substring(idx + 1))
0466: .booleanValue();
0467: key = key.substring(0, idx);
0468: }
0469:
0470: Object value = component.getAttributes().get(key);
0471: while (walk && (value == null)
0472: && (component.getParent() != null)) {
0473: component = component.getParent();
0474: value = component.getAttributes().get(key);
0475: }
0476: /*
0477: if (LogUtil.finestEnabled()) {
0478: // Trace information
0479: LogUtil.finest(this, "RESOLVING ('" + key + "') for ('"
0480: + component.getId() + "'): '" + value + "'");
0481: }
0482: */
0483: return value;
0484: }
0485: }
0486:
0487: /**
0488: * <p> This {@link VariableResolver.DataSource} tests if the given
0489: * property exists on the UIComponent. It uses the data portion of
0490: * the substitution String as a key to the UIComponent's properties
0491: * via the attribute Map.</p>
0492: */
0493: public static class HasPropertyDataSource implements DataSource {
0494: /**
0495: * <p> See class JavaDoc.</p>
0496: *
0497: * @param ctx The <code>FacesContext</code>
0498: * @param desc The <code>LayoutElement</code>
0499: * @param component The <code>UIComponent</code>
0500: * @param key The key used to obtain information from this
0501: * <code>DataSource</code>.
0502: *
0503: * @return The value resolved from key.
0504: */
0505: public Object getValue(FacesContext ctx, LayoutElement desc,
0506: UIComponent component, String key) {
0507: boolean hasKey = component.getAttributes().containsKey(key);
0508: if (!hasKey) {
0509: // Check the getter... JSF sucks when wrt attrs vs. props
0510: if (component.getAttributes().get(key) != null) {
0511: hasKey = true;
0512: }
0513: }
0514: if (!hasKey && (desc instanceof LayoutComponent)) {
0515: // In some cases, the component is a TemplateComponent child
0516: return getValue(ctx, desc.getParent(), component
0517: .getParent(), key);
0518: }
0519: return Boolean.valueOf(hasKey);
0520: }
0521: }
0522:
0523: /**
0524: * <p> This {@link VariableResolver.DataSource} tests if the given facet
0525: * exists on the UIComponent. It uses the data portion of the
0526: * substitution String as a key to the UIComponent's facets.</p>
0527: */
0528: public static class HasFacetDataSource implements DataSource {
0529: /**
0530: * <p> See class JavaDoc.</p>
0531: *
0532: * @param ctx The <code>FacesContext</code>
0533: * @param desc The <code>LayoutElement</code>
0534: * @param component The <code>UIComponent</code>
0535: * @param key The key used to obtain information from this
0536: * <code>DataSource</code>.
0537: *
0538: * @return The value resolved from key.
0539: */
0540: public Object getValue(FacesContext ctx, LayoutElement desc,
0541: UIComponent component, String key) {
0542: boolean hasFacet = component.getFacets().containsKey(key);
0543: if (!hasFacet && (desc instanceof LayoutComponent)) {
0544: // In some cases, the component is a TemplateComponent child
0545: return getValue(ctx, desc.getParent(), component
0546: .getParent(), key);
0547: }
0548: return Boolean.valueOf(hasFacet);
0549: }
0550: }
0551:
0552: /**
0553: * <p> This {@link VariableResolver.DataSource} simply returns the key
0554: * that it is given. This is useful for supplying ${}'s around the
0555: * string you wish to mark as a string. If not used, characters such
0556: * as '=' will be interpretted as a separator causing your string to
0557: * be split -- which can be very undesirable. Mostly useful in "if"
0558: * statements.</p>
0559: */
0560: public static class EscapeDataSource implements DataSource {
0561: /**
0562: * <p> See class JavaDoc.</p>
0563: *
0564: * @param ctx The <code>FacesContext</code>
0565: * @param desc The <code>LayoutElement</code>
0566: * @param component The <code>UIComponent</code>
0567: * @param key The key used to obtain information from this
0568: * <code>DataSource</code>.
0569: *
0570: * @return The value resolved from key.
0571: */
0572: public Object getValue(FacesContext ctx, LayoutElement desc,
0573: UIComponent component, String key) {
0574: return key;
0575: }
0576: }
0577:
0578: /**
0579: * <p> This {@link VariableResolver.DataSource} converts the given
0580: * <code>key</code> to a <code>Boolean</code>. This is needed because
0581: * JSF does not do this for you. When you call
0582: * <code>UIComponent.getAttributes().put(key, value)</code>,
0583: * <code>value</code> is expected to be the correct type. Often
0584: * <code>Boolean</code> types are needed. This
0585: * {@link VariableResolver.DataSource} provides a means to supply a
0586: * <code>Boolean</code> value.</p>
0587: */
0588: public static class BooleanDataSource implements DataSource {
0589: /**
0590: * <p> See class JavaDoc.</p>
0591: *
0592: * @param ctx The <code>FacesContext</code>
0593: * @param desc The <code>LayoutElement</code>
0594: * @param component The <code>UIComponent</code>
0595: * @param key The key used to obtain information from this
0596: * <code>DataSource</code>.
0597: *
0598: * @return The value resolved from key.
0599: */
0600: public Object getValue(FacesContext ctx, LayoutElement desc,
0601: UIComponent component, String key) {
0602: return Boolean.valueOf(key);
0603: }
0604: }
0605:
0606: /**
0607: * <p> This {@link VariableResolver.DataSource} uses the given
0608: * <code>key</code> to check various properties of the browser that
0609: * submitted the request. The valid keys are:</p>
0610: *
0611: * <ul><li>getUserAgent</li>
0612: * <li>getUserAgentMajor</li>
0613: * <li>isIe</li>
0614: * <li>isNav</li>
0615: * <li>isGecko</li>
0616: * <li>isSun</li>
0617: * <li>isWin</li>
0618: * <li>isIe5up</li>
0619: * <li>isIe6up</li>
0620: * <li>isNav7up</li>
0621: * <li>isIe6</li>
0622: * <li>isIe5</li>
0623: * <li>isIe4</li>
0624: * <li>isIe3</li>
0625: * <li>isNav70</li>
0626: * <li>isNav7</li>
0627: * <li>isNav6up</li>
0628: * <li>isNav6</li>
0629: * <li>isNav4up</li>
0630: * <li>isNav4</li></ul>
0631: *
0632: * @see ClientSniffer
0633: */
0634: public static class BrowserDataSource implements DataSource {
0635: /**
0636: * <p> See class JavaDoc.</p>
0637: *
0638: * @param ctx The <code>FacesContext</code>
0639: * @param desc The <code>LayoutElement</code>
0640: * @param component The <code>UIComponent</code>
0641: * @param key The key used to obtain information from this
0642: * <code>DataSource</code>.
0643: *
0644: * @return The value resolved from key.
0645: */
0646: public Object getValue(FacesContext ctx, LayoutElement desc,
0647: UIComponent component, String key) {
0648: DataSource ds = (DataSource) innerDataSources.get(key);
0649: if (ds == null) {
0650: throw new IllegalArgumentException("'" + key
0651: + "' is not a valid key for BrowserDataSource!");
0652: }
0653: return ds.getValue(ctx, desc, component, key);
0654: }
0655:
0656: /**
0657: * <p> For efficiency, we will implement the branching as a Map of
0658: * DataSources.</p>
0659: */
0660: private static Map innerDataSources = new HashMap();
0661:
0662: static {
0663: innerDataSources.put("getUserAgent", new DataSource() {
0664: public Object getValue(FacesContext ctx,
0665: LayoutElement desc, UIComponent component,
0666: String key) {
0667: return ClientSniffer.getInstance(ctx)
0668: .getUserAgent();
0669: }
0670: });
0671: innerDataSources.put("getUserAgentMajor", new DataSource() {
0672: public Object getValue(FacesContext ctx,
0673: LayoutElement desc, UIComponent component,
0674: String key) {
0675: return new Integer(ClientSniffer.getInstance(ctx)
0676: .getUserAgentMajor());
0677: }
0678: });
0679: innerDataSources.put("isIe", new DataSource() {
0680: public Object getValue(FacesContext ctx,
0681: LayoutElement desc, UIComponent component,
0682: String key) {
0683: return Boolean.valueOf(ClientSniffer.getInstance(
0684: ctx).isIe());
0685: }
0686: });
0687: innerDataSources.put("isNav", new DataSource() {
0688: public Object getValue(FacesContext ctx,
0689: LayoutElement desc, UIComponent component,
0690: String key) {
0691: return Boolean.valueOf(ClientSniffer.getInstance(
0692: ctx).isNav());
0693: }
0694: });
0695: innerDataSources.put("isGecko", new DataSource() {
0696: public Object getValue(FacesContext ctx,
0697: LayoutElement desc, UIComponent component,
0698: String key) {
0699: return Boolean.valueOf(ClientSniffer.getInstance(
0700: ctx).isGecko());
0701: }
0702: });
0703: innerDataSources.put("isSun", new DataSource() {
0704: public Object getValue(FacesContext ctx,
0705: LayoutElement desc, UIComponent component,
0706: String key) {
0707: return Boolean.valueOf(ClientSniffer.getInstance(
0708: ctx).isSun());
0709: }
0710: });
0711: innerDataSources.put("isWin", new DataSource() {
0712: public Object getValue(FacesContext ctx,
0713: LayoutElement desc, UIComponent component,
0714: String key) {
0715: return Boolean.valueOf(ClientSniffer.getInstance(
0716: ctx).isWin());
0717: }
0718: });
0719: innerDataSources.put("isIe5up", new DataSource() {
0720: public Object getValue(FacesContext ctx,
0721: LayoutElement desc, UIComponent component,
0722: String key) {
0723: return Boolean.valueOf(ClientSniffer.getInstance(
0724: ctx).isIe5up());
0725: }
0726: });
0727: innerDataSources.put("isIe6up", new DataSource() {
0728: public Object getValue(FacesContext ctx,
0729: LayoutElement desc, UIComponent component,
0730: String key) {
0731: return Boolean.valueOf(ClientSniffer.getInstance(
0732: ctx).isIe6up());
0733: }
0734: });
0735: innerDataSources.put("isNav7up", new DataSource() {
0736: public Object getValue(FacesContext ctx,
0737: LayoutElement desc, UIComponent component,
0738: String key) {
0739: return Boolean.valueOf(ClientSniffer.getInstance(
0740: ctx).isNav7up());
0741: }
0742: });
0743: innerDataSources.put("isIe6", new DataSource() {
0744: public Object getValue(FacesContext ctx,
0745: LayoutElement desc, UIComponent component,
0746: String key) {
0747: return Boolean.valueOf(ClientSniffer.getInstance(
0748: ctx).isIe6());
0749: }
0750: });
0751: innerDataSources.put("isIe5", new DataSource() {
0752: public Object getValue(FacesContext ctx,
0753: LayoutElement desc, UIComponent component,
0754: String key) {
0755: return Boolean.valueOf(ClientSniffer.getInstance(
0756: ctx).isIe5());
0757: }
0758: });
0759: innerDataSources.put("isIe4", new DataSource() {
0760: public Object getValue(FacesContext ctx,
0761: LayoutElement desc, UIComponent component,
0762: String key) {
0763: return Boolean.valueOf(ClientSniffer.getInstance(
0764: ctx).isIe4());
0765: }
0766: });
0767: innerDataSources.put("isIe3", new DataSource() {
0768: public Object getValue(FacesContext ctx,
0769: LayoutElement desc, UIComponent component,
0770: String key) {
0771: return Boolean.valueOf(ClientSniffer.getInstance(
0772: ctx).isIe3());
0773: }
0774: });
0775: innerDataSources.put("isNav70", new DataSource() {
0776: public Object getValue(FacesContext ctx,
0777: LayoutElement desc, UIComponent component,
0778: String key) {
0779: return Boolean.valueOf(ClientSniffer.getInstance(
0780: ctx).isNav70());
0781: }
0782: });
0783: innerDataSources.put("isNav7", new DataSource() {
0784: public Object getValue(FacesContext ctx,
0785: LayoutElement desc, UIComponent component,
0786: String key) {
0787: return Boolean.valueOf(ClientSniffer.getInstance(
0788: ctx).isNav7());
0789: }
0790: });
0791: innerDataSources.put("isNav6up", new DataSource() {
0792: public Object getValue(FacesContext ctx,
0793: LayoutElement desc, UIComponent component,
0794: String key) {
0795: return Boolean.valueOf(ClientSniffer.getInstance(
0796: ctx).isNav6up());
0797: }
0798: });
0799: innerDataSources.put("isNav6", new DataSource() {
0800: public Object getValue(FacesContext ctx,
0801: LayoutElement desc, UIComponent component,
0802: String key) {
0803: return Boolean.valueOf(ClientSniffer.getInstance(
0804: ctx).isNav6());
0805: }
0806: });
0807: innerDataSources.put("isNav4up", new DataSource() {
0808: public Object getValue(FacesContext ctx,
0809: LayoutElement desc, UIComponent component,
0810: String key) {
0811: return Boolean.valueOf(ClientSniffer.getInstance(
0812: ctx).isNav4up());
0813: }
0814: });
0815: innerDataSources.put("isNav4", new DataSource() {
0816: public Object getValue(FacesContext ctx,
0817: LayoutElement desc, UIComponent component,
0818: String key) {
0819: return Boolean.valueOf(ClientSniffer.getInstance(
0820: ctx).isNav4());
0821: }
0822: });
0823: }
0824: }
0825:
0826: /**
0827: * <p> This {@link VariableResolver.DataSource} converts the given
0828: * <code>key</code> to an <code>Integer</code>. This is needed
0829: * because JSF does not do this for you. When you call
0830: * <code>UIComponent.getAttributes().put(key, value)</code>,
0831: * <code>value</code> is expected to be the correct type. Often
0832: * <code>Integer</code> types are needed. This
0833: * {@link VariableResolver.DataSource} provides a means to supply an
0834: * <code>Integer</code> value.</p>
0835: */
0836: public static class IntDataSource implements DataSource {
0837: /**
0838: * <p> See class JavaDoc.</p>
0839: *
0840: * @param ctx The <code>FacesContext</code>
0841: * @param desc The <code>LayoutElement</code>
0842: * @param component The <code>UIComponent</code>
0843: * @param key The key used to obtain information from this
0844: * <code>DataSource</code>.
0845: *
0846: * @return The value resolved from key.
0847: */
0848: public Object getValue(FacesContext ctx, LayoutElement desc,
0849: UIComponent component, String key) {
0850: return Integer.valueOf(key);
0851: }
0852: }
0853:
0854: /**
0855: * <p> This {@link VariableResolver.DataSource} allows access to constants
0856: * in java classes. It expects the key to be a fully qualified Java
0857: * classname plus the variable name. Example:</p>
0858: *
0859: * <p> $constant{java.lang.Integer.MAX_VALUE} </p>
0860: */
0861: public static class ConstantDataSource implements DataSource {
0862: /**
0863: * <p> See class JavaDoc.</p>
0864: *
0865: * @param ctx The <code>FacesContext</code>
0866: * @param desc The <code>LayoutElement</code>
0867: * @param component The <code>UIComponent</code>
0868: * @param key The key used to obtain information from this
0869: * <code>DataSource</code>.
0870: *
0871: * @return The value resolved from key.
0872: */
0873: public Object getValue(FacesContext ctx, LayoutElement desc,
0874: UIComponent component, String key) {
0875: // First check to see if we've already found the value before.
0876: Object value = constantMap.get(key);
0877: if (value == null) {
0878: // Not found, lets resolve it, duplicate the old Map to avoid
0879: // sync problems
0880: Map map = new HashMap(constantMap);
0881: value = resolveValue(map, key);
0882:
0883: // Replace the shared Map w/ this new one.
0884: constantMap = map;
0885: }
0886: return value;
0887: }
0888:
0889: /**
0890: * <p> This method resolves key. Key is expected to be in the
0891: * format:</p>
0892: *
0893: * <p> some.package.Class.STATIC_VARIBLE</p>
0894: *
0895: * <p> This method will first resolve Class. It will then walk
0896: * through all its variables adding each static final variable to
0897: * the Map.</p>
0898: *
0899: * @param map The map to add variables to
0900: * @param key The fully qualified CONSTANT name
0901: *
0902: * @return The value of the CONSTANT, or null if not found
0903: */
0904: private Object resolveValue(Map map, String key) {
0905: int lastDot = key.lastIndexOf('.');
0906: if (lastDot == -1) {
0907: throw new IllegalArgumentException(
0908: "Unable to resolve '"
0909: + key
0910: + "' in $constant{"
0911: + key
0912: + "}. '"
0913: + key
0914: + "' must be a "
0915: + "fully qualified classname plus the constant name.");
0916: }
0917:
0918: // Get the classname / constant name
0919: String className = key.substring(0, lastDot);
0920:
0921: // Add all constants to the Map
0922: try {
0923: addConstants(map, Class.forName(className));
0924: } catch (ClassNotFoundException ex) {
0925: RuntimeException iae = new IllegalArgumentException(
0926: "'"
0927: + className
0928: + "' was not found! This must be a valid "
0929: + "classname. This was found in expression $constant{"
0930: + key + "}.");
0931: iae.initCause(ex);
0932: throw iae;
0933: }
0934:
0935: // The constant hopefully is in the Map now, null if not
0936: return map.get(key);
0937: }
0938:
0939: /**
0940: * This method adds all constants in the given class to the Map. The
0941: * Map key will be the fully qualified class name, plus a '.', plus
0942: * the constant name.
0943: *
0944: * @param map <code>Map</code> to store <code>cls</code>
0945: * @param cls The <code>Class</code> to store in <code>map</code>
0946: */
0947: private void addConstants(Map map, Class cls) {
0948: // Get the class name
0949: String className = cls.getName();
0950:
0951: // Get the fields
0952: Field fields[] = cls.getFields();
0953:
0954: // Add the static final fields to the Map
0955: Field field = null;
0956: for (int count = 0; count < fields.length; count++) {
0957: field = fields[count];
0958: if (Modifier.isStatic(field.getModifiers())
0959: && Modifier.isFinal(field.getModifiers())) {
0960: try {
0961: map.put(className + '.' + field.getName(),
0962: field.get(null));
0963: } catch (IllegalAccessException ex) {
0964: throw new RuntimeException(ex);
0965: }
0966: }
0967: }
0968: }
0969:
0970: /**
0971: * This embedded Map caches constant value lookups. It is static and
0972: * is shared by all users.
0973: */
0974: private static Map constantMap = new HashMap();
0975: }
0976:
0977: /**
0978: * <p> This {@link VariableResolver.DataSource} creates a MethodBinding
0979: * from the supplied key. Example:</p>
0980: *
0981: * <p> $methodBinding{#{bean.bundleKey}}</p>
0982: */
0983: public static class MethodBindingDataSource implements DataSource {
0984: /**
0985: * <p> See class JavaDoc.</p>
0986: *
0987: * @param ctx The <code>FacesContext</code>
0988: * @param desc The <code>LayoutElement</code>
0989: * @param component The <code>UIComponent</code>
0990: * @param key The key used to obtain information from this
0991: * <code>DataSource</code>.
0992: *
0993: * @return The value resolved from key.
0994: */
0995: public Object getValue(FacesContext ctx, LayoutElement desc,
0996: UIComponent component, String key) {
0997: return ctx.getApplication().createMethodBinding(key,
0998: actionArgs);
0999: }
1000: }
1001:
1002: /**
1003: * <p> This {@link VariableResolver.DataSource} allows access to resource
1004: * bundle keys. It expects the key to be a resource bundle key plus a
1005: * '.' then the actual resouce bundle key Example:</p>
1006: *
1007: * <p> $resource{bundleID.bundleKey} </p>
1008: *
1009: * <p> The bundleID should not contain '.' characters. The bundleKey
1010: * may.</p>
1011: */
1012: public static class ResourceBundleDataSource implements DataSource {
1013: /**
1014: * <p> See class JavaDoc.</p>
1015: *
1016: * @param ctx The <code>FacesContext</code>
1017: * @param desc The <code>LayoutElement</code>
1018: * @param component The <code>UIComponent</code>
1019: * @param key The key used to obtain information from this
1020: * <code>DataSource</code>.
1021: *
1022: * @return The value resolved from key.
1023: */
1024: public Object getValue(FacesContext ctx, LayoutElement desc,
1025: UIComponent component, String key) {
1026: // Get the Request attribute key
1027: int separator = key.indexOf(".");
1028: if (separator == -1) {
1029: throw new IllegalArgumentException(
1030: "'"
1031: + key
1032: + "' is not in format: \"[bundleID].[bundleKey]\"!");
1033: }
1034: String value = key.substring(0, separator);
1035:
1036: // Get the Resource Bundle
1037: ResourceBundle bundle = (ResourceBundle) ctx
1038: .getExternalContext().getRequestMap().get(value);
1039:
1040: // Make sure we have the bundle
1041: if (bundle == null) {
1042: // Should we throw an exception? For now just return the key
1043: return key;
1044: }
1045:
1046: // Return the result of the ResouceBundle lookup
1047: value = bundle.getString(key.substring(separator + 1));
1048: if (value == null) {
1049: value = key;
1050: }
1051:
1052: return value;
1053: }
1054: }
1055:
1056: /**
1057: * <p> This {@link VariableResolver.DataSource} provides access to
1058: * HttpSession attributes. It uses the data portion of the
1059: * substitution String as a key to the HttpSession Map.</p>
1060: */
1061: public static class SessionDataSource implements DataSource {
1062: /**
1063: * <p> See class JavaDoc.</p>
1064: *
1065: * @param ctx The <code>FacesContext</code>
1066: * @param desc The <code>LayoutElement</code>
1067: * @param component The <code>UIComponent</code>
1068: * @param key The key used to obtain information from this
1069: * <code>DataSource</code>.
1070: *
1071: * @return The value resolved from key.
1072: */
1073: public Object getValue(FacesContext ctx, LayoutElement desc,
1074: UIComponent component, String key) {
1075: return ctx.getExternalContext().getSessionMap().get(key);
1076: }
1077: }
1078:
1079: /**
1080: * <p> This {@link VariableResolver.DataSource} retrieves style classes
1081: * from the current {@link com.sun.rave.web.ui.theme.Theme}. The data
1082: * portion of the substitution String as the
1083: * {@link com.sun.rave.web.ui.theme.Theme} key.</p>
1084: */
1085: public static class StyleDataSource implements DataSource {
1086: /**
1087: * <p> See class JavaDoc.</p>
1088: *
1089: * @param ctx The <code>FacesContext</code>
1090: * @param desc The <code>LayoutElement</code>
1091: * @param component The <code>UIComponent</code>
1092: * @param key The key used to obtain information from this
1093: * <code>DataSource</code>.
1094: *
1095: * @return The value resolved from key.
1096: */
1097: public Object getValue(FacesContext ctx, LayoutElement desc,
1098: UIComponent component, String key) {
1099: return ThemeUtilities.getTheme(ctx).getStyleClass(key);
1100: }
1101: }
1102:
1103: /**
1104: * <p> This {@link VariableResolver.DataSource} retrieves Theme messages
1105: * from the {@link com.sun.rave.web.ui.theme.Theme}. The data portion of
1106: * the substitution String is the {@link com.sun.rave.web.ui.theme.Theme}
1107: * message key.</p>
1108: */
1109: public static class ThemeDataSource implements DataSource {
1110: /**
1111: * <p> See class JavaDoc.</p>
1112: *
1113: * @param ctx The <code>FacesContext</code>
1114: * @param desc The <code>LayoutElement</code>
1115: * @param component The <code>UIComponent</code>
1116: * @param key The key used to obtain information from this
1117: * <code>DataSource</code>.
1118: *
1119: * @return The value resolved from key.
1120: */
1121: public Object getValue(FacesContext ctx, LayoutElement desc,
1122: UIComponent component, String key) {
1123: return ThemeUtilities.getTheme(ctx).getMessage(key);
1124: }
1125: }
1126:
1127: /**
1128: * <p> This {@link VariableResolver.DataSource} retrieves the path to a
1129: * JavaScript file from the {@link com.sun.rave.web.ui.theme.Theme}. The
1130: * data portion of the substitution String is the
1131: * {@link com.sun.rave.web.ui.theme.Theme} key for the JavaScript file.</p>
1132: */
1133: public static class ThemeJavaScriptDataSource implements DataSource {
1134: /**
1135: * <p> See class JavaDoc.</p>
1136: *
1137: * @param ctx The <code>FacesContext</code>
1138: * @param desc The <code>LayoutElement</code>
1139: * @param component The <code>UIComponent</code>
1140: * @param key The key used to obtain information from this
1141: * <code>DataSource</code>.
1142: *
1143: * @return The value resolved from key.
1144: */
1145: public Object getValue(FacesContext ctx, LayoutElement desc,
1146: UIComponent component, String key) {
1147: return ThemeUtilities.getTheme(ctx).getPathToJSFile(key);
1148: }
1149: }
1150:
1151: /**
1152: * <p> This {@link VariableResolver.DataSource} provides access to
1153: * DisplayField values. It uses the data portion of the substitution
1154: * String as the DisplayField name to find. This is a non-qualified
1155: * DisplayField name. It will walk up the View tree starting at the
1156: * View object cooresponding to the LayoutElement which contained this
1157: * expression. At each ContainerView, it will look for a child with
1158: * a matching name.</p>
1159: public static class DisplayFieldDataSource implements DataSource {
1160: public Object getValue(FacesContext ctx, LayoutElement desc,
1161: UIComponent component, String key) {
1162: while (desc != null) {
1163: View view = desc.getView(ctx);
1164: if (view instanceof ContainerView) {
1165: View child = null;
1166: //FIXME: use a better way to find if 'key' is a child of 'view'
1167: try {
1168: child = (((ContainerView)(view)).getChild(key));
1169: } catch (Exception ex) {
1170: }
1171: if (child != null) {
1172: return ((ContainerView) view).getDisplayFieldValue(key);
1173: }
1174: }
1175: desc = desc.getParent();
1176: }
1177: return null;
1178: }
1179: }
1180: */
1181:
1182: /**
1183: * <p> This class provides an implementation for the syntax $this{xyz}
1184: * where xyz can be any of the following.</p>
1185: *
1186: * <ul><li>component -- Current <code>UIComponent</code></li>
1187: * <li>clientId -- Current <code>UIComponent</code>'s client id</li>
1188: * <li>id -- Current <code>UIComponent</code>'s id</li>
1189: * <li>layoutElement -- Current {@link LayoutElement}</li>
1190: * <li>parent -- Parent <code>UIComponent</code></li>
1191: * <li>parentId -- Parent <code>UIComponent</code>'s client id</li>
1192: * <li>parentLayoutElement -- Parent {@link LayoutElement}</li>
1193: * <li>namingContainer -- Nearest <code>NamingContainer</code></li>
1194: * <li>valueBinding -- <code>ValueBinding</code> representing the
1195: * <code>UIComponent</code></li>
1196: * </ul>
1197: */
1198: public static class ThisDataSource implements DataSource {
1199: /**
1200: * <p> See class JavaDoc.</p>
1201: *
1202: * @param ctx The <code>FacesContext</code>
1203: * @param desc The <code>LayoutElement</code>
1204: * @param comp The <code>UIComponent</code>
1205: * @param key The key used to obtain information from this
1206: * <code>DataSource</code>.
1207: *
1208: * @return The value resolved from key.
1209: */
1210: public Object getValue(FacesContext ctx, LayoutElement desc,
1211: UIComponent comp, String key) {
1212: Object value = null;
1213:
1214: if ((key.equalsIgnoreCase(CLIENT_ID))
1215: || (key.length() == 0)) {
1216: value = comp.getClientId(ctx);
1217: } else if (key.equalsIgnoreCase(ID)) {
1218: value = comp.getId();
1219: } else if (key.equalsIgnoreCase(COMPONENT)) {
1220: value = comp;
1221: } else if (key.equalsIgnoreCase(LAYOUT_ELEMENT)) {
1222: value = desc;
1223: } else if (key.equalsIgnoreCase(PARENT_ID)) {
1224: value = comp.getParent().getId();
1225: } else if (key.equalsIgnoreCase(PARENT_CLIENT_ID)) {
1226: value = comp.getParent().getClientId(ctx);
1227: } else if (key.equalsIgnoreCase(PARENT)) {
1228: value = comp.getParent();
1229: } else if (key.equalsIgnoreCase(PARENT_LAYOUT_ELEMENT)) {
1230: value = desc.getParent();
1231: } else if (key.equalsIgnoreCase(NAMING_CONTAINER)) {
1232: for (value = comp.getParent(); value != null; value = ((UIComponent) value)
1233: .getParent()) {
1234: if (value instanceof NamingContainer) {
1235: break;
1236: }
1237: }
1238: } else if (key.equalsIgnoreCase(VALUE_BINDING)) {
1239: // Walk backward up the tree generate the path
1240: Stack stack = new Stack();
1241: String id = null;
1242: // FIXME: b/c of a bug, the old behavior actually returned the
1243: // FIXME: parent component... the next line is here to persist
1244: // FIXME: this behavior b/c some code depends on this, fix this
1245: // FIXME: when you have a chance.
1246: comp = comp.getParent();
1247: while ((comp != null) && !(comp instanceof UIViewRoot)) {
1248: id = comp.getId();
1249: if (id == null) {
1250: // Generate an id based on the clientId
1251: id = comp.getClientId(ctx);
1252: id = id
1253: .substring(id
1254: .lastIndexOf(NamingContainer.SEPARATOR_CHAR) + 1);
1255: }
1256: stack.push(id);
1257: comp = comp.getParent();
1258: }
1259: StringBuffer buf = new StringBuffer();
1260: buf.append("view");
1261: while (!stack.empty()) {
1262: buf.append("." + stack.pop());
1263: }
1264: value = buf.toString();
1265: } else {
1266: throw new IllegalArgumentException("'" + key
1267: + "' is not valid in $this{" + key + "}.");
1268: }
1269:
1270: return value;
1271: }
1272:
1273: /**
1274: * <p> Defines "component" in $this{component}. Returns the
1275: * UIComponent object.</p>
1276: */
1277: public static final String COMPONENT = "component";
1278:
1279: /**
1280: * <p> Defines "clientId" in $this{clientId}. Returns
1281: * the String representing the client id for the UIComponent.</p>
1282: */
1283: public static final String CLIENT_ID = "clientId";
1284:
1285: /**
1286: * <p> Defines "id" in $this{id}. Returns the String representing
1287: * the id for the UIComponent.</p>
1288: */
1289: public static final String ID = "id";
1290:
1291: /**
1292: * <p> Defines "layoutElement" in $this{layoutElement}. Returns
1293: * the LayoutElement.</p>
1294: */
1295: public static final String LAYOUT_ELEMENT = "layoutElement";
1296:
1297: /**
1298: * <p> Defines "parent" in $this{parent}. Returns the
1299: * parent UIComponent object.</p>
1300: */
1301: public static final String PARENT = "parent";
1302:
1303: /**
1304: * <p> Defines "parentId" in $this{parentId}. Returns the
1305: * parent UIComponent object's Id.</p>
1306: */
1307: public static final String PARENT_ID = "parentId";
1308:
1309: /**
1310: * <p> Defines "parentClientId" in $this{parentClientId}. Returns the
1311: * parent UIComponent object's client Id.</p>
1312: */
1313: public static final String PARENT_CLIENT_ID = "parentClientId";
1314:
1315: /**
1316: * <p> Defines "parentLayoutElement" in $this{parentLayoutElement}.
1317: * Returns the parent LayoutElement.</p>
1318: */
1319: public static final String PARENT_LAYOUT_ELEMENT = "parentLayoutElement";
1320:
1321: /**
1322: * <p> Defines "namingContainer" in $this{namingContainer}. Returns
1323: * the nearest naming container object (i.e. the form).</p>
1324: */
1325: public static final String NAMING_CONTAINER = "namingContainer";
1326:
1327: /**
1328: * <p> Defines "valueBinding" in $this{valueBinding}. Returns
1329: * a <code>ValueBinding</code> to this UIComponent.</p>
1330: */
1331: public static final String VALUE_BINDING = "valueBinding";
1332: }
1333:
1334: /**
1335: * The main function for this class provides some simple test cases.
1336: *
1337: * @param args The commandline arguments.
1338: */
1339: public static void main(String args[]) {
1340: String test = null;
1341: String good = null;
1342:
1343: test = ""
1344: + VariableResolver.resolveVariables(null, null, null,
1345: "$escape($escape(LayoutElement))", "$", "(",
1346: ")");
1347: good = "LayoutElement";
1348: System.out.println("Expected Result: '" + good + "'");
1349: System.out.println(" Result: '" + test + "'");
1350: if (!test.equals(good)) {
1351: System.out.println("FAILED!!!!");
1352: }
1353:
1354: test = ""
1355: + VariableResolver.resolveVariables(null, null, null,
1356: "$escape($escape(EEPersistenceManager))", "$",
1357: "(", ")");
1358: good = "EEPersistenceManager";
1359: System.out.println("Expected Result: '" + good + "'");
1360: System.out.println(" Result: '" + test + "'");
1361: if (!test.equals(good)) {
1362: System.out.println("FAILED!!!!");
1363: }
1364:
1365: test = ""
1366: + VariableResolver.resolveVariables(null, null, null,
1367: "$es$cape$escape(EEPersistenceManager))", "$",
1368: "(", ")");
1369: good = "$es$capeEEPersistenceManager)";
1370: System.out.println("Expected Result: '" + good + "'");
1371: System.out.println(" Result: '" + test + "'");
1372: if (!test.equals(good)) {
1373: System.out.println("FAILED!!!!");
1374: }
1375:
1376: test = ""
1377: + VariableResolver.resolveVariables(null, null, null,
1378: "$escape($escapeEEP$ersistenceManager))", "$",
1379: "(", ")");
1380: good = "$escapeEEP$ersistenceManager)";
1381: System.out.println("Expected Result: '" + good + "'");
1382: System.out.println(" Result: '" + test + "'");
1383: if (!test.equals(good)) {
1384: System.out.println("FAILED!!!!");
1385: }
1386:
1387: test = ""
1388: + VariableResolver.resolveVariables(null, null, null,
1389: "$escape($escape(EEPersistenceManager)))", "$",
1390: "(", ")");
1391: good = "EEPersistenceManager)";
1392: System.out.println("Expected Result: '" + good + "'");
1393: System.out.println(" Result: '" + test + "'");
1394: if (!test.equals(good)) {
1395: System.out.println("FAILED!!!!");
1396: }
1397:
1398: test = ""
1399: + VariableResolver.resolveVariables(null, null, null,
1400: "$escape($escape(EEPersistenceManager())", "$",
1401: "(", ")");
1402: good = "$escape(EEPersistenceManager()";
1403: System.out.println("Expected Result: '" + good + "'");
1404: System.out.println(" Result: '" + test + "'");
1405: if (!test.equals(good)) {
1406: System.out.println("FAILED!!!!");
1407: }
1408:
1409: test = ""
1410: + VariableResolver.resolveVariables(null, null, null,
1411: "$escape($escape($escape(EEPersistenceManager()))==$escape("
1412: + "EEPersistenceManager()))", "$", "(",
1413: ")");
1414: good = "EEPersistenceManager()==EEPersistenceManager()";
1415: System.out.println("Expected Result: '" + good + "'");
1416: System.out.println(" Result: '" + test + "'");
1417: if (!test.equals(good)) {
1418: System.out.println("FAILED!!!!");
1419: }
1420:
1421: test = ""
1422: + VariableResolver.resolveVariables(null, null, null,
1423: "$escape($escape($escape(EEPersistenceManager()))==$escape("
1424: + "EEPersistenceManager()))", "$", "(",
1425: ")");
1426: good = "EEPersistenceManager()==EEPersistenceManager()";
1427: System.out.println("Expected Result: '" + good + "'");
1428: System.out.println(" Result: '" + test + "'");
1429: if (!test.equals(good)) {
1430: System.out.println("FAILED!!!!");
1431: }
1432:
1433: /*
1434: for (int x = 0; x < 100000; x++) {
1435: System.out.println("" + VariableResolver.resolveVariables(
1436: null, null, null,
1437: "$escape($escape(EEPers" + x + "istenceManager()))==$escape("
1438: + "EEPersistenceManager())", "$", "(", ")"));
1439: }
1440: */
1441: }
1442:
1443: /**
1444: * <p> Contains the {@link VariableResolver.DataSource}'s for
1445: * $<type>{<variable>} syntax.</p>
1446: */
1447: private static Map dataSourceMap = new HashMap();
1448:
1449: /**
1450: * <p> Defines "attribute" in $attribute{...}. This allows you to
1451: * retrieve an HttpRequest attribute.</p>
1452: */
1453: public static final String ATTRIBUTE = "attribute";
1454:
1455: /**
1456: * <p> Defines "pageSession" in $pageSession{...}. This allows you to
1457: * retrieve a PageSession attribute.</p>
1458: public static final String PAGE_SESSION = "pageSession";
1459: */
1460:
1461: /**
1462: * <p> Defines "property" in $property{...}. This allows you to
1463: * retrieve a property from the UIComponent.</p>
1464: */
1465: public static final String PROPERTY = "property";
1466:
1467: /**
1468: * <p> Defines "hasProperty" in $hasProperty{...}. This allows you to
1469: * see if a property from the UIComponent exists.</p>
1470: */
1471: public static final String HAS_PROPERTY = "hasProperty";
1472:
1473: /**
1474: * <p> Defines "hasFacet" in $hasFacet{...}. This allows you to
1475: * see if a facet from the UIComponent exists.</p>
1476: */
1477: public static final String HAS_FACET = "hasFacet";
1478:
1479: /**
1480: * <p> Defines "session" in $session{...}. This allows you to retrieve
1481: * an HttpSession attribute.
1482: */
1483: public static final String SESSION = "session";
1484:
1485: /**
1486: * <p> Defines "style" in $style{...}. This allows you to retrieve
1487: * a style classes from the current {@link com.sun.rave.web.ui.theme.Theme}.
1488: */
1489: public static final String STYLE = "style";
1490:
1491: /**
1492: * <p> Defines "theme" in $theme{...}. This allows you to
1493: * retrieve a Theme message from the current
1494: * {@link com.sun.rave.web.ui.theme.Theme}.
1495: */
1496: public static final String THEME = "theme";
1497:
1498: /**
1499: * <p> Defines "themeScript" in $themeScript{...}. This allows you to
1500: * retrieve a JavaScript classes from the current
1501: * {@link com.sun.rave.web.ui.theme.Theme}.
1502: */
1503: public static final String THEME_JS = "themeScript";
1504:
1505: /**
1506: * <p> Defines "requestParameter" in $requestParameter{...}. This allows
1507: * you to retrieve a HttpRequest parameter (QUERY_STRING
1508: * parameter).</p>
1509: */
1510: public static final String REQUEST_PARAMETER = "requestParameter";
1511:
1512: /**
1513: * <p> Defines "display" in $display{...}. This allows you to retrive
1514: * a DisplayField value.</p>
1515: public static final String DISPLAY = "display";
1516: */
1517:
1518: /**
1519: * <p> Defines "this" in $this{...}. This allows you to retrieve a
1520: * number of different objects related to the relative placement of
1521: * this expression.</p>
1522: *
1523: * @see ThisDataSource
1524: */
1525: public static final String THIS = "this";
1526:
1527: /**
1528: * <p> Defines "escape" in $escape{...}. This allows some reserved
1529: * characters to be escaped in "if" attributes. Such as '=' or
1530: * '|'.</p>
1531: */
1532: public static final String ESCAPE = "escape";
1533:
1534: /**
1535: * <p> Defines "boolean" in $boolean{...}. This converts the given
1536: * String to a Boolean.</p>
1537: */
1538: public static final String BOOLEAN = "boolean";
1539:
1540: /**
1541: * <p> Defines "browser" in $browser{...}. This checks properties of the
1542: * browser that sent the request.</p>
1543: */
1544: public static final String BROWSER = "browser";
1545:
1546: /**
1547: * <p> Defines "int" in $int{...}. This converts the given String to an
1548: * Integer.</p>
1549: */
1550: public static final String INT = "int";
1551:
1552: /**
1553: * <p> Defines "methodBinding" in $methodBinding{...}. This allows
1554: * MethodBindings in to be created.</p>
1555: */
1556: public static final String METHOD_BINDING = "methodBinding";
1557:
1558: /**
1559: * <p> Defines "constant" in $constant{...}. This allows constants
1560: * in java classes to be accessed.</p>
1561: */
1562: public static final String CONSTANT = "constant";
1563:
1564: /**
1565: * <p> Defines "resource" in $resource{...}. This allows resource
1566: * to be accessed.</p>
1567: */
1568: public static final String RESOURCE = "resource";
1569:
1570: // Static initializer to setup DataSources
1571: static {
1572: AttributeDataSource attrDS = new AttributeDataSource();
1573: dataSourceMap.put(ATTRIBUTE, attrDS);
1574: dataSourceMap.put("", attrDS);
1575: // dataSourceMap.put(PAGE_SESSION, new PageSessionDataSource());
1576: dataSourceMap.put(PROPERTY, new PropertyDataSource());
1577: dataSourceMap.put(HAS_PROPERTY, new HasPropertyDataSource());
1578: dataSourceMap.put(HAS_FACET, new HasFacetDataSource());
1579: dataSourceMap.put(SESSION, new SessionDataSource());
1580: dataSourceMap.put(STYLE, new StyleDataSource());
1581: dataSourceMap.put(THEME, new ThemeDataSource());
1582: dataSourceMap.put(THEME_JS, new ThemeJavaScriptDataSource());
1583: dataSourceMap.put(REQUEST_PARAMETER,
1584: new RequestParameterDataSource());
1585: // dataSourceMap.put(DISPLAY, new DisplayFieldDataSource());
1586: dataSourceMap.put(THIS, new ThisDataSource());
1587: dataSourceMap.put(ESCAPE, new EscapeDataSource());
1588: dataSourceMap.put(INT, new IntDataSource());
1589: dataSourceMap.put(BOOLEAN, new BooleanDataSource());
1590: dataSourceMap.put(BROWSER, new BrowserDataSource());
1591: dataSourceMap.put(CONSTANT, new ConstantDataSource());
1592: dataSourceMap.put(RESOURCE, new ResourceBundleDataSource());
1593: dataSourceMap
1594: .put(METHOD_BINDING, new MethodBindingDataSource());
1595: }
1596:
1597: /**
1598: * Constant defining the arguments required for a Action MethodBinding.
1599: */
1600: private static Class actionArgs[] = { ActionEvent.class };
1601:
1602: /**
1603: * The '$' character marks the beginning of a substituion in a String.
1604: */
1605: public static final String SUB_START = "$";
1606:
1607: /**
1608: * The '(' character marks the beginning of the data content of a String
1609: * substitution.
1610: */
1611: public static final String SUB_TYPE_DELIM = "{";
1612:
1613: /**
1614: * The ')' character marks the end of the data content for a String
1615: * substitution.
1616: */
1617: public static final String SUB_END = "}";
1618: }
|