/*
* #!
* Ontopia Engine
* #-
* 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.utils;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
import net.ontopia.topicmaps.core.NameIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.TopicNameIF;
import net.ontopia.topicmaps.core.VariantNameIF;
import net.ontopia.utils.GrabberStringifier;
import net.ontopia.utils.StringifierIF;
/**
* PUBLIC: Creates stringifiers that extract strings representing
* names from topics, according to various criteria, including scope.
*/
public class TopicStringifiers {
private static final StringifierIF<TopicIF> DEFAULT_STRINGIFIER
= new GrabberStringifier<TopicIF, NameIF>(TopicCharacteristicGrabbers.getDisplayNameGrabber(),
new NameStringifier());
private TopicStringifiers() {
// don't call me
}
/**
* PUBLIC: Gets a stringifier that will return a default name for each
* topic it is applied to.
*
* @return stringifierIF; the string this returns will be the
* display name of its given topic if present, otherwise
* the least constrained topic name.
*/
public static StringifierIF<TopicIF> getDefaultStringifier() {
return DEFAULT_STRINGIFIER;
}
/**
* PUBLIC: Gets a stringifier that will return the topic name it
* determines to match the given scope best. There are no guarantees
* as to <em>which</em> topic name it will return if more than one
* topic name match equally well.
*
* @param scope collection of TopicIF objects; the given scope
* @return stringifierIF; the string this stringifier returns will be a
* topic name of its given topic, selected according to the
* logic in TopicNameGrabber.
*/
public static StringifierIF<TopicIF> getTopicNameStringifier(Collection<TopicIF> scope) {
return new GrabberStringifier<TopicIF, TopicNameIF>(new TopicNameGrabber(scope),
new NameStringifier());
}
/**
* PUBLIC: Gets a stringifier that will return
* the variant that it determines to match the given scope
* best. There are no guarantees as to <em>which</em> variant name
* it will return if more than one variant name matches equally well.
*
* @param scope collection of TopicIF objects; the given scope
* @return stringifierIF; the string this stringifier returns will be a
* variant name of its given topic, selected according to the
* logic in VariantNameGrabber.
*/
public static StringifierIF<TopicIF> getVariantNameStringifier(Collection<TopicIF> scope) {
return new GrabberStringifier<TopicIF, VariantNameIF>(new TopicVariantNameGrabber(scope),
new NameStringifier());
}
/**
* PUBLIC: Gets a stringifier that will return the sort names of
* topics, when they have one. If the topics have no sort name,
* one of the topic names will be used instead.
*
* @return StringifierIF; the string this stringifier returns will be a
* variant name of its given topic, selected according to the
* logic in SortNameGrabber.
* @since 1.1
*/
public static StringifierIF<TopicIF> getSortNameStringifier() {
return new GrabberStringifier<TopicIF, NameIF>(TopicCharacteristicGrabbers.getSortNameGrabber(),
new NameStringifier());
}
/**
* PUBLIC: Gets a fast stringifier that will return the sort names
* of topics, when they have one. If the topics have no sort name,
* one of the topic names will be used instead. This stringifier is
* the one used by tolog.
* @since 5.1.0
*/
public static StringifierIF<TopicIF> getFastSortNameStringifier(TopicMapIF tm) {
return new FastSortNameStringifier(tm);
}
/**
* PUBLIC: Gets a stringifier that will return the name it
* determines matches the given scopes best. There is no guarantee
* as to <em>which</em> name it will return if more than one name
* matches equally well.
*
* @param tnscope collection of TopicIF objects; the scope applied to
* topic names
* @param vnscope collection of TopicIF objects; the scope applied to
* variant names
* @return the configured stringifier
* @since 1.3.2
*/
public static StringifierIF<TopicIF> getStringifier(Collection<TopicIF> tnscope,
Collection<TopicIF> vnscope) {
if (tnscope == null || tnscope.isEmpty()) {
if (vnscope == null || vnscope.isEmpty())
return getDefaultStringifier();
else
return getVariantNameStringifier(vnscope);
} else if (vnscope == null || vnscope.isEmpty())
return getTopicNameStringifier(tnscope);
else
return new GrabberStringifier<TopicIF, NameIF>(new NameGrabber(tnscope, vnscope, false),
new NameStringifier());
}
/**
* PUBLIC: Returns the default name of the topic.
*
* @since 2.0
*/
public static String toString(TopicIF topic) {
return DEFAULT_STRINGIFIER.toString(topic);
}
// FIXME: The following methods must later be optimized by
// refactoring stringifier code into static methods, so that we do
// not allocate new objects every time.
/**
* PUBLIC: Returns the name of the topic given the specified
* topic name theme.
*
* @since 2.0
*/
public static String toString(TopicIF topic, TopicIF tntheme) {
StringifierIF<TopicIF> strfy = getStringifier(Collections.singleton(tntheme), null);
return strfy.toString(topic);
}
/**
* PUBLIC: Returns the name of the topic given the specified
* topic name theme.
*
* @since 2.0
*/
public static String toString(TopicIF topic, Collection<TopicIF> tnscope) {
StringifierIF<TopicIF> strfy = getStringifier(tnscope, null);
return strfy.toString(topic);
}
/**
* PUBLIC: Returns the name of the topic given the specified
* topic name and variant name themes.
*
* @since 2.0
*/
public static String toString(TopicIF topic, TopicIF tntheme, TopicIF vntheme) {
StringifierIF<TopicIF> strfy = getStringifier(Collections.singleton(tntheme), Collections.singleton(vntheme));
return strfy.toString(topic);
}
/**
* PUBLIC: Returns the name of the topic given the specified
* topic name and variant name themes.
*
* @since 2.0
*/
public static String toString(TopicIF topic, Collection<TopicIF> tnscope, TopicIF vntheme) {
StringifierIF<TopicIF> strfy = getStringifier(tnscope, Collections.singleton(vntheme));
return strfy.toString(topic);
}
/**
* PUBLIC: Returns the name of the topic given the specified
* topic name and variant name themes.
*
* @since 2.0
*/
public static String toString(TopicIF topic, Collection<TopicIF> tnscope, Collection<TopicIF> vnscope) {
StringifierIF<TopicIF> strfy = getStringifier(tnscope, vnscope);
return strfy.toString(topic);
}
// ===== INTERNAL
public static class FastSortNameStringifier implements StringifierIF<TopicIF> {
private TopicIF defnametype;
private TopicIF sort;
public FastSortNameStringifier(TopicMapIF tm) {
this.defnametype = tm.getTopicBySubjectIdentifier(PSI.getSAMNameType());
this.sort = tm.getTopicBySubjectIdentifier(PSI.getXTMSort());
}
public String toString(TopicIF topic) {
// 0: verify that we have a topic at all
if (topic == null)
return "[No name]";
// 1: pick base name with the fewest topics in scope
// (and avoid typed names)
TopicNameIF bn = null;
int least = 0xEFFF;
Collection<TopicNameIF> bns = topic.getTopicNames();
if (!bns.isEmpty()) {
Iterator<TopicNameIF> it = bns.iterator();
while (it.hasNext()) {
TopicNameIF candidate = it.next();
int score = candidate.getScope().size() * 10;
if (candidate.getType() != defnametype)
score++;
if (score < least) {
bn = candidate;
least = score;
}
}
}
if (bn == null)
return "[No name]";
// 2: if we have a sort name, pick variant with fewest topics in scope
// beyond sort name; penalty for no sort name = 0xFF topics
if (sort == null)
return bn.getValue();
VariantNameIF vn = null;
least = 0xEFFF;
Collection<VariantNameIF> vns = bn.getVariants();
if (!vns.isEmpty()) {
Iterator<VariantNameIF> it = vns.iterator();
while (it.hasNext()) {
VariantNameIF candidate = it.next();
Collection<TopicIF> scope = candidate.getScope();
int themes;
if (scope.contains(sort))
themes = scope.size() - 1;
else
themes = 0xFF + scope.size();
if (themes < least) {
vn = candidate;
least = themes;
}
}
}
if (vn == null || vn.getValue() == null)
return bn.getValue();
return vn.getValue();
}
}
}