/*
* #!
* Ontopia Webed
* #-
* 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.webed.taglibs.form;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import net.ontopia.topicmaps.core.TMObjectIF;
import net.ontopia.topicmaps.nav2.core.NavigatorPageIF;
import net.ontopia.topicmaps.nav2.impl.framework.InteractionELSupport;
import net.ontopia.topicmaps.nav2.utils.FrameworkUtils;
import net.ontopia.topicmaps.nav2.utils.Stringificator;
import net.ontopia.topicmaps.webed.impl.utils.TagUtils;
import org.apache.velocity.VelocityContext;
/**
* INTERNAL: Custom tag that represents a list to select one or
* multiple values from (aka drop-down and scrolling menus) in an
* input form.
*/
public class ListTag extends TagSupport {
// initialization of logging facility
private static final String CATEGORY_NAME = ListTag.class.getName();
/**
* The default location where the velocity template can be retrieved
* from.
*/
protected final static String TEMPLATE_FILE = "list.vm";
// --- Tag Attributes
protected String id;
protected String readonly;
protected String klass;
protected String action_name;
protected String params;
protected String collection_name;
protected String selected_name;
protected String type = "dropdown"; // default type
protected String unspecified = "unspecified"; // default label
/**
* Process the start tag, do nothing.
*
* @return <code>EVAL_BODY_INCLUDE</code>
*/
public int doStartTag() {
return EVAL_BODY_INCLUDE;
}
/**
* Generate the required select tag.
*
* @exception JspException if a JSP exception has occurred
*/
public int doEndTag() throws JspException {
// original value; used to detect changes
Set value = new HashSet();
// already selected objects
Collection selectedObjs = java.util.Collections.EMPTY_LIST;
if (selected_name == null)
value.add("-1"); // equals choosing "-- unspecified --"
else {
selectedObjs = InteractionELSupport
.extendedGetValue(selected_name, pageContext);
// create collection to store in
Iterator it = selectedObjs.iterator();
while (it.hasNext())
value.add(((TMObjectIF) it.next()).getObjectId());
if (value.isEmpty())
value.add(null); // this is how empty sets look to ProcessServlet
}
// multiselect is a special case; if nothing's selected there, then
// null is the value we get, not -1; see bug #1252
if ((type.equals("multiselect") || type.equals("scrolling")) && selected_name == null)
value = java.util.Collections.singleton(null);
boolean readonly = TagUtils.isComponentReadOnly(pageContext, this.readonly);
VelocityContext vc = TagUtils.getVelocityContext(pageContext);
// register action data and produce input field name
if (action_name != null && !readonly) {
// retrieve the action group
String group_name = TagUtils.getActionGroup(pageContext);
if (group_name == null)
throw new JspException("list tag has no action group available.");
String name = TagUtils.registerData(pageContext, action_name, group_name,
params, value);
if (!(type.equals("multiselect") || type.equals("scrolling"))&& !unspecified.equals("none"))
vc.put("unspecified", unspecified);
vc.put("name", name);
}
if (id != null) vc.put("id", id);
vc.put("readonly", new Boolean(readonly));
if (klass != null) vc.put("class", klass);
vc.put("type", type);
// retrieve the elements
Collection elements = new ArrayList();
// since collection_name is a required attribute no extra validation needed
NavigatorPageIF contextTag = FrameworkUtils.getContextTag(pageContext);
if (contextTag == null)
throw new JspException("List tag found no logic:context ancestor tag");
Collection tmObjs = InteractionELSupport.extendedGetValue(collection_name,
pageContext);
Iterator iter = tmObjs.iterator();
while (iter.hasNext()) {
Object obj = iter.next();
if (!(obj instanceof TMObjectIF))
throw new JspException("Collection in variable " + collection_name +
"contained non-topic map object: " + obj);
TMObjectIF tmObj = (TMObjectIF) obj;
elements.add(new NameIdContainer(tmObj.getObjectId(),
Stringificator.toString(contextTag, tmObj),
selectedObjs.contains(tmObj)));
}
vc.put("elements", elements);
// all variables are now set, proceed with outputting
TagUtils.processWithVelocity(pageContext, TEMPLATE_FILE, pageContext.getOut(), vc);
// Continue processing this page
return EVAL_BODY_INCLUDE;
}
/**
* Release any acquired resources.
*/
public void release() {
super.release();
id = null;
readonly = null;
action_name = null;
params = null;
collection_name = null;
selected_name = null;
type = "dropdown";
}
// ------------------------------------------------------------
// tag attribute accessors
// ------------------------------------------------------------
/**
* Sets the id of the tag. This value will be used as the value of
* an ID attribute in the generated output.
*/
public void setId(String id) {
this.id = id;
}
/**
* Sets the the readonly flag of the tag.
*/
public void setReadonly(String readonly) {
this.readonly = readonly;
}
/**
* Sets the class attribute of the tag. This value will be used as
* the value of the 'class' attribute in the generated output.
*/
public void setClass(String klass) {
this.klass = klass;
}
/**
* Sets the name of the related action (required).
*/
public void setAction(String action_name) {
this.action_name = action_name;
}
/**
* Sets the variable name(s) of the parameter(s) transmitted to the
* action separated by whitespaces (optional).
*/
public void setParams(String params) {
this.params = params;
}
/**
* Sets the variable name of the collection that contains the
* candidates for the object's value.
*/
public void setCollection(String collection_name) {
this.collection_name = collection_name;
}
/**
* Sets the variable name of the currently set value.
*/
public void setSelected(String selected_name) {
this.selected_name = selected_name;
}
/**
* Sets which type of menu should be used when presenting the
* selection list (allowed values are "dropdown" and "scrolling").
*
* <p>Note: If 'type' is not set, "dropdown" will be the default
* behaviour.
*/
public void setType(String type) {
if (type.equalsIgnoreCase("dropdown")
|| type.equalsIgnoreCase("scrolling")
|| type.equalsIgnoreCase("multiselect")
|| type.equalsIgnoreCase("checkbox")
|| type.equalsIgnoreCase("radio"))
this.type = type;
else
throw new IllegalArgumentException("ListTag: type '" + type + "' " +
"is not an allowed value.");
}
/**
* Tells the tag what text to use for the '-unspecified-' item at
* the top of the list. If set to 'none' there will be no
* unspecified item. The default is '-unspecified-' as it was
* before.
*/
public void setUnspecified(String unspecified) {
this.unspecified = unspecified;
}
// -------------------------------------------------------------------
// Internal class which holds one information element for the list,
// this makes access from the velocity template very easy.
// -------------------------------------------------------------------
public final class NameIdContainer {
private String id;
private String name;
private boolean selected;
public NameIdContainer(String id, String name, boolean selected) {
this.id = id;
this.name = name;
this.selected = selected;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public boolean getSelected() {
return selected;
}
}
}