/*
* #!
* 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.TMvalue;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.BodyContent;
import net.ontopia.topicmaps.core.AssociationIF;
import net.ontopia.topicmaps.core.AssociationRoleIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.nav2.core.ContextManagerIF;
import net.ontopia.topicmaps.nav2.core.NavigatorRuntimeException;
import net.ontopia.utils.CollectionMap;
import net.ontopia.utils.CollectionUtils;
import net.ontopia.utils.DeciderIF;
import net.ontopia.topicmaps.nav2.core.ScopeSupportIF;
import net.ontopia.topicmaps.nav2.impl.basic.AssocInfoStorage;
import net.ontopia.topicmaps.nav2.impl.basic.AssocInfoStorageComparator;
import net.ontopia.topicmaps.nav2.utils.ScopeUtils;
import net.ontopia.topicmaps.nav2.taglibs.logic.ContextTag;
import net.ontopia.topicmaps.nav2.utils.FrameworkUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: Special Tag for iterating over a sorted collection of
* triples (Association Type, Association Role Type, Association),
* creating new content for each iteration.
*/
public class AssociationTypeLoopTag extends BodyTagSupport implements ScopeSupportIF {
// initialization of logging facility
private static Logger log = LoggerFactory
.getLogger(AssociationTypeLoopTag.class.getName());
// members
private ContextManagerIF ctxtMgr;
private AssocInfoStorage[] assocStore; // collection we loop over
private int index; // current index we are in the loop
// tag attributes
private String varNameTopic;
private String varNameAssociations;
private String varNameAT;
private String varNameART;
private boolean useUserContextFilter = false;
/**
* Process the start tag for this instance.
*/
public int doStartTag() throws JspTagException {
ContextTag contextTag = FrameworkUtils.getContextTag(pageContext);
ctxtMgr = contextTag.getContextManager();
// get collection which contains topic to work with
Collection coll = null;
if (varNameTopic != null)
coll = ctxtMgr.getValue(varNameTopic);
else
coll = ctxtMgr.getDefaultValue();
// establish new lexical scope for this loop
ctxtMgr.pushScope();
// do not proceed if no elements in collection at all
if (coll.isEmpty()) {
log.debug("Cannot retrieve topic from empty collection in " +
(varNameTopic != null ? varNameTopic : "<default>") );
return SKIP_BODY;
}
// get first element in collection which should be a topic instance
TopicIF topic = null;
try {
topic = (TopicIF) CollectionUtils.getFirstElement(coll);
} catch (Exception e) {
throw new NavigatorRuntimeException("AssociationTypeLoopTag: Could not retrieve " +
"topic from " + (varNameTopic != null ?
varNameTopic : "<default>"), e);
}
// get scope decider based on the user context filer
// FIXME: Should avoid creating a new decider for every loop.
DeciderIF scopeDecider = null;
if (useUserContextFilter)
scopeDecider = ScopeUtils.getScopeDecider(pageContext, contextTag,
SCOPE_ASSOCIATIONS);
// create list of ordered associations for this topic
assocStore = createOrderedAssociationTypes(topic, scopeDecider);
if (assocStore == null || assocStore.length == 0) {
log.info("No associations available for topic " + (varNameTopic != null ?
varNameTopic : "<default>"));
return SKIP_BODY;
}
// set first element of collection in the beginning
setVariableValues(assocStore[0]);
index = 1;
return EVAL_BODY_BUFFERED;
}
/**
* Actions after some body has been evaluated.
*/
public int doAfterBody() throws JspTagException {
// put out the evaluated body
BodyContent body = getBodyContent();
JspWriter out = null;
try {
out = body.getEnclosingWriter();
out.print(body.getString());
body.clearBody();
} catch(IOException ioe) {
throw new NavigatorRuntimeException("Error in AssociationTypeLoopTag.", ioe);
}
// test if we have to repeat the body
if (index < assocStore.length) {
// set to next value in list
setVariableValues(assocStore[index]);
index++;
return EVAL_BODY_AGAIN;
} else {
return SKIP_BODY;
}
}
/**
* Process the end tag.
*/
public int doEndTag() throws JspException {
// establish old lexical scope, back to outside of the loop
ctxtMgr.popScope();
// reset members
ctxtMgr = null;
assocStore = null;
// index = 0;
return EVAL_PAGE;
}
/**
* reset the state of the Tag.
*/
public void release() {
// overwrite default behaviour
// do not set parent to null!!!
}
// -----------------------------------------------------------------
// set methods for tag attributes
// -----------------------------------------------------------------
public void setName(String varNameTopic) {
this.varNameTopic = varNameTopic;
}
public void setSetAssociations(String varNameAssociations) {
this.varNameAssociations = varNameAssociations;
}
public void setSetAT(String varNameAT) {
this.varNameAT = varNameAT;
}
public void setSetART(String varNameART) {
this.varNameART = varNameART;
}
/**
* INTERNAL: sets up if the tag should use the context filter which is
* implict contained in the user session. Default behaviour is to
* not use the user context filter. Allowed values are:
* <ul>
* <li>off</li>
* <li>user</li>
* </ul>
*/
public void setContextFilter(String contextFilter) {
if (contextFilter.indexOf("off") != -1)
this.useUserContextFilter = false;
if (contextFilter.indexOf("user") != -1)
this.useUserContextFilter = true;
}
// -----------------------------------------------------------------
// helper methods
// -----------------------------------------------------------------
/**
* INTERNAL: set variable values with help of the context manager
*/
private void setVariableValues(AssocInfoStorage ais) {
// set variables in current lexical scope
if (varNameAssociations != null)
ctxtMgr.setValue(varNameAssociations, ais.getAssociations());
if (varNameAT != null)
ctxtMgr.setValue(varNameAT, ais.getType());
if (varNameART != null) {
if (ais.getRoleType() != null)
ctxtMgr.setValue(varNameART, ais.getRoleType());
else
ctxtMgr.setValue(varNameART, Collections.EMPTY_LIST);
}
}
/**
* INTERNAL: create array of triples (AssocInfoStorage objects) and
* order them by the name of the association type (in scope of the
* association role type).
*/
private AssocInfoStorage[] createOrderedAssociationTypes(TopicIF topic, DeciderIF _scopeDec) {
// contains AssocInfoStorage objects in the first instance without
// associations assigned
Set assocTypeStore = new HashSet();
// contains as key: AssocInfoStorage object and value: associations
CollectionMap assocMap = new CollectionMap();
Iterator it = topic.getRoles().iterator();
// get all association this topic is involved as a role player
while (it.hasNext()) {
AssociationRoleIF assocRole = (AssociationRoleIF) it.next();
AssociationIF assoc = assocRole.getAssociation();
if (_scopeDec != null && !_scopeDec.ok(assoc))
continue;
TopicIF assocType = assoc.getType();
TopicIF assocRoleType = assocRole.getType();
AssocInfoStorage ais = new AssocInfoStorage(assocType, assocRoleType);
assocTypeStore.add(ais);
assocMap.add(ais, assoc);
}
// order
AssocInfoStorageComparator assocStoreComparator = new AssocInfoStorageComparator();
AssocInfoStorage[] assocList = new AssocInfoStorage[assocTypeStore.size()];
assocTypeStore.toArray(assocList);
Arrays.sort( assocList, assocStoreComparator);
// assign associations
for (int i=0; i < assocList.length; i++) {
AssocInfoStorage ais = assocList[i];
ais.setAssociations((Collection) assocMap.get(ais));
}
return assocList;
}
}