/*
* #!
* Ontopia Navigator
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed 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 net.ontopia.topicmaps.nav2.taglibs.tolog;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import net.ontopia.topicmaps.core.TMObjectIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.nav2.core.ContextManagerIF;
import net.ontopia.topicmaps.nav2.core.NavigatorRuntimeException;
import net.ontopia.topicmaps.nav2.taglibs.logic.ContextTag;
import net.ontopia.topicmaps.nav2.utils.FrameworkUtils;
import net.ontopia.topicmaps.nav2.utils.NavigatorUtils;
import net.ontopia.topicmaps.query.core.DeclarationContextIF;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: Tolog Tag for executing a query,
* iterating over each object in a result collection and
* creating new content for each iteration.
*/
public class SetTag extends QueryExecutingTag { //BodyTagSupport {
private static final long serialVersionUID = -3009179502068590303L;
// initialization of logging facility
private static Logger log = LoggerFactory.getLogger(SetTag.class.getName());
// FIXME: replace this ugliness with a Map (but wait for tests)
private static final String scopeNames[] = {
"application",
"session",
"page",
"request"
};
private static final int scopes[] = {
PageContext.APPLICATION_SCOPE,
PageContext.SESSION_SCOPE,
PageContext.PAGE_SCOPE,
PageContext.REQUEST_SCOPE
};
// members
Collection outValue;
String clonedVar;
// tag attributes
private String reqparam;
private String scope;
private Object value;
private String var;
/**
* Default constructor.
*/
public SetTag() {
super();
}
/**
* Process the start tag for this instance.
*/
public int doStartTag() throws JspTagException {
ContextTag contextTag = FrameworkUtils.getContextTag(pageContext);
if (contextTag == null)
throw new JspTagException("<tolog:set> must be nested directly or"
+ " indirectly within a <tolog:context> tag, but no"
+ " <tolog:context> was found.");
ContextManagerIF ctxmgr = contextTag.getContextManager();
// Get the TopicMap from the context.
TopicMapIF topicmap = contextTag.getTopicMap();
// Check that a valid combination of input parameters has been used.
// Otherwise, JspTagException is thrown with a suitable error message.
validateInputAttributes();
clonedVar = var;
if (query == null) {
outValue = new ArrayList();
if (reqparam != null) {
String ids[] = pageContext.getRequest().getParameterValues(reqparam);
if (ids == null || ids.length == 0)
outValue = Collections.EMPTY_LIST;
else {
if (topicmap == null)
throw new JspTagException("<tolog:set> found no topic map");
DeclarationContextIF declarationContext = contextTag.getDeclarationContext();
for (int i= 0; i < ids.length; i++) {
TMObjectIF tempOutValue =
NavigatorUtils.stringID2Object(topicmap, ids[i], declarationContext);
if (tempOutValue != null)
outValue.add(tempOutValue);
}
}
} else if (value != null) {
if (value instanceof Collection)
outValue.addAll((Collection)value);
else
outValue.add(value);
} else
return EVAL_BODY_BUFFERED;
} else {
log.debug("Set found query");
// Create a QueryProcessorIF for the topicmap.
if (topicmap == null)
throw new JspTagException("<tolog:set> found no topic map");
super.doStartTag();
if (queryResult.getWidth() == 0)
throw new NavigatorRuntimeException("<tolog:set> : got a query result"
+ " with zero columns, instead of the expected: one."
+ "\nPlease check the query.");
if (queryResult.getWidth() >= 2)
throw new NavigatorRuntimeException("<tolog:set> : got a query result"
+ " with more than one column, instead of the expected: one."
+ " Please check the query.");
// Get 'outValue' from the first column of 'queryResult'.
outValue = getColumn(queryResult, 0);
// If no variable name given, use the first (only) column from the query.
if (var == null)
clonedVar = queryResult.getColumnName(0);
}
return SKIP_BODY;
}
/**
* Check that the a correct combination of input parameters has been used.
*/
private void validateInputAttributes() throws JspTagException {
if (query == null) {
if (var == null)
throw new JspTagException("<tolog:set> : requires a 'var'- or a"
+ " 'query'-attribute (or both), but got neither.");
if (!(reqparam == null || value == null))
throw new JspTagException("<tolog:set> : requires either a"
+ " 'query'-, a 'reqparam'- or a 'value'-attribute,"
+ " but got both.");
} else {
if (reqparam != null || value != null)
throw new JspTagException("<tolog:set> : requires either a"
+ " 'query'-, a 'reqparam'- or a 'value'-attribute,"
+ " but got more than one of them.");
}
}
/**
* Set a variable in the jstl environment to a given value.
*/
private void setJstl(String var, Object val, int scope) {
pageContext.setAttribute(var, val, scope);
}
/**
* Set a variable in the ontopia environment to a given value.
*/
private void setOntopia(String var, Object val, ContextManagerIF ctxmgr) {
if (val instanceof Object[]) {
Object[] jstlArray = (Object[])val;
// Let the context manager create a collection to hold the array values.
// Need one value to do this, so use first value in jstlArray (or null).
ctxmgr.setValue(var, (jstlArray.length == 0)
? null
: jstlArray[0]);
Collection ontopiaValue = ctxmgr.getValue(var);
// Add the rest of the values in jstlArray to ontopiaValue.
for (int i = 1; i < jstlArray.length; i++)
ontopiaValue.add(jstlArray[i]);
} else if (val instanceof Collection) {
ctxmgr.setValue(var, (Collection)val);
} else {
ctxmgr.setValue(var, val);
}
}
private static int mapScope(String scopeName) throws JspTagException {
for (int i = 0; i < scopeNames.length; i++)
if (scopeName.equals(scopeNames[i]))
return scopes[i];
throw new JspTagException("Unrecognised scope attribute in <tolog:set ..."
+ "scope=\"" + scopeName + "\"");
}
/**
* Get each value of a given column from queryResult, and make a collection
* with the contents of that column.
*/
private Collection getColumn(QueryResultIF queryResult, int column) {
Collection retVal = new ArrayList();
while (queryResult.next()) {
retVal.add(queryResult.getValue(column));
}
return retVal;
}
/**
* Actions after some body has been evaluated.
*/
public int doAfterBody() throws JspTagException {
outValue = new ArrayList();
// FIXME: It would be nice if the following if-test could be true only for
// bodies that are actually empty (not the ones that just generate a
// zero-length string).
if (!getBodyContent().getString().equals(""))
outValue.add(getBodyContent().getString());
return SKIP_BODY;
}
/**
* Process the end tag.
*/
public int doEndTag() throws JspException {
// Bind 'outValue' to var in appropriate scope.
if (scope == null || scope.equals("ontopia") || scope.equals("oks")) {
ContextTag contextTag = FrameworkUtils.getContextTag(pageContext);
ContextManagerIF ctxmgr = contextTag.getContextManager();
setOntopia(clonedVar, outValue, ctxmgr);
} else
setJstl(clonedVar, outValue, mapScope(scope));
return EVAL_PAGE;
}
/**
* Resets the state of the Tag.
*/
public void release() {
// do *not* reset tag attributes
}
// -----------------------------------------------------------------
// Set methods for tag attributes
// -----------------------------------------------------------------
public void setReqparam(String reqparam) {
this.reqparam = reqparam;
}
public void setScope(String scope) {
this.scope = scope;
}
public void setVar(String var) {
this.var = var;
}
public void setValue(Object value) {
this.value = value;
}
}