/* * #! * 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.net.MalformedURLException; import java.util.ArrayList; import java.util.HashSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import javax.servlet.jsp.JspTagException; import net.ontopia.utils.DeciderIF; import net.ontopia.utils.CollectionUtils; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.infoset.impl.basic.URILocator; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.OccurrenceIF; import net.ontopia.topicmaps.core.ScopedIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.TopicMapIF; import net.ontopia.topicmaps.core.TopicNameIF; import net.ontopia.topicmaps.core.TypedIF; import net.ontopia.topicmaps.core.VariantNameIF; import net.ontopia.topicmaps.nav2.core.NavigatorCompileException; import net.ontopia.topicmaps.nav2.core.NavigatorDeciderIF; import net.ontopia.topicmaps.nav2.core.NavigatorRuntimeException; import net.ontopia.topicmaps.nav2.core.NavigatorTagException; import net.ontopia.topicmaps.utils.SupersetOfContextDecider; import net.ontopia.topicmaps.nav2.utils.NavigatorUtils; import net.ontopia.topicmaps.nav2.taglibs.value.BaseValueProducingAndAcceptingTag; import net.ontopia.topicmaps.nav2.impl.basic.DeciderIFWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * INTERNAL: Value Producing Tag for taking an input collection * and filtering it, passing only some of the elements on * to its parent (could be a set tag or another filter element). */ public class FilterTag extends BaseValueProducingAndAcceptingTag { // constants public static final String CLASS_TOPICMAP = "topicmap"; public static final String CLASS_TOPIC = "topic"; public static final String CLASS_ASSOC = "association"; public static final String CLASS_OCC = "occurrence"; public static final String CLASS_BASENAME = "basename"; public static final String CLASS_VARIANT = "variant"; public static final String CLASS_ROLE = "role"; public static final String CLASS_LOCATOR = "locator"; // initialization of logging facility private static Logger log = LoggerFactory .getLogger(FilterTag.class.getName()); // constants public static final String UNTYPED = "{NONE}"; // tag attributes private String instanceOf; private String deciderClassName; private NavigatorDeciderIF decider; private String inScopeOfValue; private String isKind; private boolean invert = false; private boolean randomElement = false; public Collection process(Collection tmObjects) throws JspTagException { Collection result = null; // check first if unique attribute settings int paramCount = ((instanceOf!=null) ? 1 : 0) + ((deciderClassName!=null) ? 1 : 0) + ((isKind!=null) ? 1 : 0) + ((inScopeOfValue!=null) ? 1 : 0) + ((randomElement) ? 1 : 0); if (paramCount != 1) throw new NavigatorCompileException("FilterTag: Ambiguous attribute " + "settings (" + paramCount +" attrs "+ "specified, must be exactly 1)."); // do the fun part if (instanceOf != null) result = filterInstanceOf(tmObjects, instanceOf); else if (deciderClassName != null) { decider = getDeciderInstance(deciderClassName); if (decider == null) throw new NavigatorCompileException("FilterTag: Could not retrieve " + "decider instance for " + deciderClassName); result = filterWithDecider(tmObjects, decider); } else if (inScopeOfValue != null) result = filterInScopeOf(tmObjects, inScopeOfValue); else if (isKind != null) result = filterIs(tmObjects, isKind); else if (randomElement) { result = Collections.singleton(CollectionUtils.getRandom(tmObjects)); } return result; } // ----------------------------------------------------------------- // internal helper / working methods // ----------------------------------------------------------------- /** * INTERNAL: Create a new collection which consists only of objects * that are instances of the topic with the given URI as its * subject indicator. */ private Collection filterInstanceOf(Collection tmObjects, String instanceOf) throws NavigatorTagException { Collection filtered = new HashSet(); // log.debug("filterInstanceOf (" + instanceOf + "): " + tmObjects); // separate typed from untyped objects if (instanceOf.equals(UNTYPED)) { if (tmObjects != null) { Iterator iter = tmObjects.iterator(); Object obj; while (iter.hasNext()) { obj = iter.next(); if ((obj instanceof TypedIF) && ( (((TypedIF) obj).getType() == null && !invert) || (((TypedIF) obj).getType() != null && invert))) filtered.add(obj); else if ((obj instanceof TopicIF) && ( (((TopicIF) obj).getTypes().isEmpty() && !invert) || (((TopicIF) obj).getTypes().isEmpty() && invert))) filtered.add(obj); } // while } } else { Collection classes = null; // --- 1st try: interpret <instanceOf> as an URI try { classes = new ArrayList(); // get Topic for filtering by subject indicator TopicMapIF topicmap = contextTag.getTopicMap(); if (topicmap == null) throw new NavigatorRuntimeException("FilterTag found no topic map."); TopicIF topic = topicmap.getTopicBySubjectIdentifier(new URILocator(instanceOf)); if (topic != null) classes.add(topic); } catch (MalformedURLException e) { // --- 2nd try: interpret <instanceOf> as variable name classes = contextTag.getContextManager().getValue(instanceOf); } if (tmObjects != null) { Iterator iter = tmObjects.iterator(); Object obj; while (iter.hasNext()) { obj = iter.next(); // ---- for a single-typed object: if (obj instanceof TypedIF && ( (classes.contains(((TypedIF) obj).getType()) && !invert) || (!classes.contains(((TypedIF) obj).getType()) && invert) )) { filtered.add(obj); } // ---- for a topic: else if (obj instanceof TopicIF) { Iterator it = ((TopicIF) obj).getTypes().iterator(); while (it.hasNext()) { TopicIF type = (TopicIF) it.next(); if ((classes.contains(type) && !invert) || (!classes.contains(type) && invert)) { filtered.add(obj); break; } } // while it } } // while iter } } // log.debug("filter result " + filtered); return filtered; } /** * INTERNAL: Creates a filtered collection with the help of * the specified decider class. */ private Collection filterWithDecider(Collection tmObjects, NavigatorDeciderIF decider) { Collection filtered = new HashSet(); if (tmObjects != null) { Iterator iter = tmObjects.iterator(); Object obj; while (iter.hasNext()) { obj = iter.next(); if (decider.ok(contextTag, obj)) { if (!invert) { filtered.add(obj); // log.debug("not invert - ok - " + obj); } else { // log.debug("invert - ok - ! " + obj); } } else { if (invert) { filtered.add(obj); // log.debug("invert - not ok - " + obj); } else { // log.debug("not invert - ok - ! " + obj); } } } // while } return filtered; } /** * INTERNAL: Create a new collection which consists only of objects * which have the topic with the given URI as a theme in their scope, * or which have a topic in the named collection in their scopes. */ private Collection filterInScopeOf(Collection<ScopedIF> tmObjects, String inScopeOf) throws NavigatorCompileException { Collection context = new ArrayList(1); // --- 1st try: interpret <inScopeOf> as an URI TopicIF topic = NavigatorUtils.stringID2Topic(contextTag.getTopicMap(), inScopeOf); if (topic != null) { context.add(topic); } else { // --- 2nd try: interpret <inScopeOf> as variable name context = contextTag.getContextManager().getValue(inScopeOf); } Collection filtered = new HashSet(); if (tmObjects != null) { SupersetOfContextDecider decider = new SupersetOfContextDecider(context); Iterator<ScopedIF> iter = tmObjects.iterator(); ScopedIF obj; while (iter.hasNext()) { obj = iter.next(); if (decider.ok(obj)) { if (!invert) filtered.add(obj); } else { if (invert) filtered.add(obj); } } // while } return filtered; } /** * INTERNAL: test if objects of input collection are a (Java) * instance of a specific class (OccurrenceIF, TopicIF, TopicMapIF, * AssociationIF, TopicNameIF, VariantIF, LocatorIF, * AssociationRoleIF). */ private Collection filterIs(Collection tmObjects, String kind) throws NavigatorCompileException { // TODO: Make kind an int and get class from class array by kind index. // what are we filtering by? Class klass = null; if (kind.equalsIgnoreCase(CLASS_OCC)) klass = OccurrenceIF.class; else if (kind.equalsIgnoreCase(CLASS_TOPIC)) klass = TopicIF.class; else if (kind.equalsIgnoreCase(CLASS_TOPICMAP)) klass = TopicMapIF.class; else if (kind.equalsIgnoreCase(CLASS_ASSOC)) klass = AssociationIF.class; else if (kind.equalsIgnoreCase(CLASS_BASENAME)) klass = TopicNameIF.class; else if (kind.equalsIgnoreCase(CLASS_VARIANT)) klass = VariantNameIF.class; else if (kind.equalsIgnoreCase(CLASS_LOCATOR)) klass = LocatorIF.class; else if (kind.equalsIgnoreCase(CLASS_ROLE)) klass = AssociationRoleIF.class; else throw new NavigatorCompileException("FilterTag got wrong value for the" + " kind attribute: '" + klass + "'"); // do the filtering if (tmObjects == null) return Collections.EMPTY_SET; else { Collection filtered = new HashSet(tmObjects.size()); Iterator iter = tmObjects.iterator(); while (iter.hasNext()) { Object obj = iter.next(); if ((klass.isInstance(obj) && !invert) || (!klass.isInstance(obj) && invert)) filtered.add(obj); } return filtered; } } // ----------------------------------------------------------------- // set methods for tag attributes // ----------------------------------------------------------------- /** * Sets instanceOf string, which will first interpreted as an URI * for retrieving a topic by it's subject indicator. If this is without * success instanceOf will interpreted as a variable name. * <p> * Special string "{NONE}" will be interpreted to retrieve all untyped * instances. */ public void setInstanceOf(String instanceOf) { this.instanceOf = instanceOf; } public void setDecider(String classname) { this.deciderClassName = classname; } public void setInScopeOf(String inScopeOfValue) { this.inScopeOfValue = inScopeOfValue; } public void setInvert(String invert) { if (invert.equalsIgnoreCase("true") || invert.equalsIgnoreCase("yes") ) this.invert = true; else this.invert = false; } /** * Set the behaviour of the filter to choose a random element from * the given input collection. * * @since 1.3.2 */ public void setRandomElement(String randomElement) { if (randomElement.equalsIgnoreCase("true") || randomElement.equalsIgnoreCase("yes") ) this.randomElement = true; else this.randomElement = false; } /** * Sets the kind of topic map objects that should be passed on. * * @param kind String which should contain one of the following * values: topicmap | association | occurrence | topic | * basename | variant | role | locator */ public void setIs(String kind) { if (kind.equalsIgnoreCase(CLASS_TOPICMAP) || kind.equalsIgnoreCase(CLASS_ASSOC) || kind.equalsIgnoreCase(CLASS_OCC) || kind.equalsIgnoreCase(CLASS_TOPIC) || kind.equalsIgnoreCase(CLASS_LOCATOR) || kind.equalsIgnoreCase(CLASS_ROLE) || kind.equalsIgnoreCase(CLASS_BASENAME) || kind.equalsIgnoreCase(CLASS_VARIANT)) this.isKind = kind; else throw new IllegalArgumentException("Invalid value '" + kind + "' in attribute 'is' " + " of tag 'filter'."); } // --------------------------------------------------------------- // internal methods // --------------------------------------------------------------- public NavigatorDeciderIF getDeciderInstance(String classname) throws NavigatorRuntimeException { Object obj = null; try { // Create decider instance obj = contextTag.getNavigatorApplication().getInstanceOf(classname); // if instance of DeciderIF we need to wrap in NavigatorDeciderWrapper if (obj instanceof NavigatorDeciderIF) return (NavigatorDeciderIF) obj; else if (obj instanceof DeciderIF) return new DeciderIFWrapper((DeciderIF)obj); } catch (NavigatorRuntimeException e) { log.warn("Unable to retrieve instance of " + classname); } // We weren't able to create an instance so let's return null. return null; } }