blob: 7b1bcd9b296e75dd4f0852ae5973f9d01a081394 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.taglibs.rdc.dm;
import java.text.MessageFormat;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.el.VariableResolver;
import org.apache.commons.el.ExpressionEvaluatorImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.taglibs.rdc.RDCUtils;
import org.apache.taglibs.rdc.core.BaseModel;
import org.apache.taglibs.rdc.core.ComponentModel;
import org.apache.taglibs.rdc.core.Constants;
import org.apache.taglibs.rdc.core.GroupModel;
import org.apache.taglibs.rdc.core.GroupTag;
/**
* Utility methods for the dm package
*
* @author Rahul Akolkar
*/
public class DMUtils {
// ******************
// PRIVATE CONSTANTS
// ******************
private static final String NESTED_DATAMODEL_SEPARATOR = "|";
private static final String FILLER = ".*";
private static final String ID_TOK = "\\$((\\w|_|\\" +
NESTED_DATAMODEL_SEPARATOR + ")+)";
private static final String START_EXPR = "\\#\\{";
private static final String FILLER_NO_END_EXPR = "[^\\}]*";
private static final Pattern PATTERN_ID =
Pattern.compile(FILLER + ID_TOK + FILLER);
private static final int ID_GROUP = 1;
private static final Pattern PATTERN_OPEN_EXPR =
Pattern.compile(FILLER + START_EXPR + FILLER_NO_END_EXPR);
// Error messages (to be i18n'zed)
private static final String ERR_NO_SUCH_MODEL = "No model for " +
"\"{0}\" found as child of \"{1}\"";
private static final String ERR_NULL_EXPR = "EL error, \"{0}\"" +
"; JspContext or expression or return type is null";
private static final String ERR_BAD_EXPR = "EL error while " +
"evaluating expression: \"{0}\"";
// Logging
private static Log log = LogFactory.getLog(DMUtils.class);
// ******************
// PUBLIC METHODS
// ******************
/**
* Return true if the given component or container is done.
*
* @param model The {@link BaseModel} to check.
*/
public static boolean isChildDone(BaseModel model) {
if (model instanceof GroupModel) {
GroupModel grpModel = (GroupModel) model;
if (grpModel.getGroupConfirm() != null) {
if (grpModel.getState() == Constants.GRP_STATE_DONE) {
return true;
}
} else {
if (grpModel.getActiveChildren().size() == 0) {
grpModel.setState(Constants.GRP_STATE_DONE);
return true;
}
}
return false;
} else if (model instanceof ComponentModel &&
model.getState() == Constants.FSM_DONE) {
return true;
} else if (model instanceof BaseModel &&
model.getState() == Constants.FSM_DONE) {
return true;
}
return false;
}
// ******************
// PACKAGE METHODS
// ******************
/**
* Activate child so it takes over the dialog from the next turn
*/
static int invokeDormantChild(Map children, List activeChildren,
String id) {
if (id == null) {
log.info("Null ID of identified target");
return Constants.GRP_ALL_CHILDREN_DONE;
}
BaseModel model = (BaseModel) children.get(id);
if (model != null) {
if (model.getState() == Constants.FSM_DORMANT) {
activeChildren.add(id);
if (model instanceof BaseModel) {
model.setState(Constants.FSM_INPUT);
} else if (model instanceof GroupModel) {
model.setState(Constants.GRP_STATE_RUNNING);
} else if (model instanceof ComponentModel) {
model.setState(Constants.FSM_INPUT);
}
}
return Constants.GRP_SOME_CHILD_RUNNING;
} else {
log.info("ID \"" + id + "\" of target in group dialog " +
"configuration is invalid");
return Constants.GRP_ALL_CHILDREN_DONE;
}
}
/**
* Two pass evaluation:
* 1) Substitute value-of RDCs
* 2) Evaluate using the EL evaluator
*
* @param String to be evaluated
*/
static Object proprietaryEval(GroupTag groupTag, GroupModel groupModel,
String expr, Class retClass, LinkedHashMap lruCache,
List tempVars) {
Matcher matcher = PATTERN_ID.matcher(expr);
while (matcher.matches()) {
String matched = matcher.group(ID_GROUP);
int start = expr.lastIndexOf(matched) - 1; // -1 for $
int end = start + 1 + matched.length();
String startStr = "", endStr = "";
if (start > 0) {
startStr = expr.substring(0, start);
}
if (end < expr.length()) {
endStr = expr.substring(end);
}
Object[] exprData = null;
Object rdcValue = null;
String pageVar = null;
if (lruCache != null && lruCache.containsKey(matched)) {
exprData = (Object[])lruCache.get(matched);
rdcValue = exprData[0];
pageVar = (String)exprData[1];
}
if (exprData == null) {
rdcValue = valueOfIDExpr(groupModel, matched);
pageVar = RDCUtils.getNextRDCVarName();
tempVars.add(pageVar);
addToCache(lruCache, matched, rdcValue, pageVar);
}
groupTag.getJspContext().setAttribute(pageVar, rdcValue);
expr = wrapAsNeeded(startStr, pageVar, endStr);
matcher = PATTERN_ID.matcher(expr);
}
return valueOfELExpr(groupTag.getJspContext(), expr, retClass);
}
// ******************
// PRIVATE METHODS
// ******************
/**
* LRU Cache implementation using LinkedHashMap
*
*/
private static void addToCache(LinkedHashMap cache,
String key, Object value, String var) {
final int MAX_CACHE_SIZE = 50;
if (cache.size() == MAX_CACHE_SIZE) {
cache.remove(cache.keySet().iterator().next());
}
cache.put(key, new Object[] { value, var });
}
/**
* Make sure the temporary page context variable gets evaluated in an
* EL expression
*
*/
private static String wrapAsNeeded(String startStr, String pageVar,
String endStr) {
Matcher openExpr = PATTERN_OPEN_EXPR.matcher(startStr);
if (openExpr.matches()) {
return startStr + pageVar + endStr;
}
return startStr + "#{" + pageVar + "}" + endStr;
}
/**
* Obtain the value of the given ID with respect to the given GroupModel.
*
* @param GroupModel The group model which holds a child of the given ID
* @param String The ID whose value is required
*/
private static Object valueOfIDExpr(GroupModel groupModel, String id) {
Object value = null;
BaseModel iteratorModel = groupModel;
if (id == null) {
return null;
}
StringTokenizer idTokenizer = new StringTokenizer(id,
NESTED_DATAMODEL_SEPARATOR);
BaseModel model = null;
while (idTokenizer.hasMoreTokens()) {
String idTok = idTokenizer.nextToken();
if (iteratorModel instanceof GroupModel) {
model = (BaseModel) ((GroupModel)iteratorModel).
getLocalMap().get(idTok);
} else if (iteratorModel instanceof ComponentModel) {
model = (BaseModel) ((ComponentModel)iteratorModel).
getLocalMap().get(idTok);
}
if (model == null) {
// Invalid child ID specified in XML navigation rules
MessageFormat msgFormat = new MessageFormat(ERR_NO_SUCH_MODEL);
log.warn(msgFormat.format(new Object[] {idTok,
iteratorModel.getId()}));
return "$" + id;
}
iteratorModel = model;
}
value = model.getValue();
return value;
}
/**
* Obtain the value of the given JSP 2.0 EL expression in the group
* tag's PageContext.
*
* @param JspContext The context in which the expression should be
* evaluated
* @param String The JSP 2.0 EL expression
* @param Class retType The expected return type
*/
private static Object valueOfELExpr(JspContext ctx, String expr_,
Class retType) {
if (ctx == null || expr_ == null || retType == null) {
MessageFormat msgFormat = new MessageFormat(ERR_NULL_EXPR);
log.warn(msgFormat.format(new Object[] {expr_}));
return null;
}
String expr = expr_.replaceAll("\\#\\{", "\\$\\{");
Object value = null;
if (retType == null) {
retType = java.lang.String.class;
}
ExpressionEvaluatorImpl exprEvaluator = new ExpressionEvaluatorImpl();
VariableResolver varResolver = ctx.getVariableResolver();
try {
value = exprEvaluator.evaluate(expr, retType, varResolver, null);
} catch (javax.servlet.jsp.el.ELException e) {
MessageFormat msgFormat = new MessageFormat(ERR_BAD_EXPR);
log.warn(msgFormat.format(new Object[] {e.getMessage()}), e);
return null;
} // end of try-catch
return value;
}
}