/* * #! * Ontopia Vizigator * #- * 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.viz; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.nav.utils.comparators.TopicComparator; import net.ontopia.utils.OntopiaRuntimeException; public class VizTopicTypePriorityConfigManager { VizTopicMapConfigurationManager tmConfig; private TopicIF rankTopic; private TopicIF rankAssociationType; private TopicIF rankAboveRoleType; private TopicIF rankBelowRoleType; private TopicIF defaultTypePrecedenceTopic; private static TopicComparator topicComparator = new TopicComparator(); private static final String RANK_TOPIC = VizTopicMapConfigurationManager.BASE + "rank-topic"; private static final String DEFAULT_TYPE_PRECEDENCE_TOPIC = VizTopicMapConfigurationManager.BASE + "default-precedence-topic"; private static final String RANK_ASSOCIATION = VizTopicMapConfigurationManager.BASE + "rank-association"; private static final String RANK_ABOVE_ROLE = VizTopicMapConfigurationManager.BASE + "rank-above-role"; private static final String RANK_BELOW_ROLE = VizTopicMapConfigurationManager.BASE + "rank-below-role"; public VizTopicTypePriorityConfigManager( VizTopicMapConfigurationManager tmConfig) { this.tmConfig = tmConfig; rankTopic = tmConfig.getTopic(RANK_TOPIC); defaultTypePrecedenceTopic = tmConfig.getTopic(DEFAULT_TYPE_PRECEDENCE_TOPIC); rankAssociationType = tmConfig.getTopic(RANK_ASSOCIATION); rankAboveRoleType = tmConfig.getTopic(RANK_ABOVE_ROLE); rankBelowRoleType = tmConfig.getTopic(RANK_BELOW_ROLE); if (rankTopic.getRolesByType(rankBelowRoleType).isEmpty()) { // Initialize the priority circuit with rankTopic. AssociationIF newRankAssociation = tmConfig.builder.makeAssociation(rankAssociationType); tmConfig.builder.makeAssociationRole(newRankAssociation, rankBelowRoleType, rankTopic); tmConfig.builder.makeAssociationRole(newRankAssociation, rankAboveRoleType, rankTopic); } if (!isRanked(defaultTypePrecedenceTopic)) rankLast(defaultTypePrecedenceTopic); } /** * Get the topic ranked above or below this topic. * If 'up' is true, will return the topic ranked above. * Otherwise it will return the topic ranked below. * @param source The topic ranked above/below target (pending 'up'). * @param up Indicates whether to return the below or above topic. * @return The topic ranked below or above this topic. */ private TopicIF getNeighbourRanked(TopicIF source, boolean up) { AssociationRoleIF sourceRole = getRankRole(source, up); AssociationIF association = getRankAssociation(sourceRole); AssociationRoleIF targetRole = getRankRole(association, up); TopicIF target = targetRole.getPlayer(); if (target == null) throw new OntopiaRuntimeException("Error in configuration: Every" + " rank-below-role must have a player."); return target; } /** * Get the the above/below rank role played by a given topic. * @param source The topic to get the rank role from. * @param up Indicates whether to get the role moving up or down in rank. * @return the the above/below rank role played by 'source'. */ private AssociationRoleIF getRankRole(TopicIF source, boolean up) { TopicIF sourceRoleType = up ? rankBelowRoleType : rankAboveRoleType; // Get the above/below rank role played by the given topic. Collection rankRoles = source.getRolesByType(sourceRoleType); if (rankRoles.size() != 1) { String nextTo = up ? "below" : "above"; if (rankRoles.isEmpty()) throw new OntopiaRuntimeException("Error in configuration: Missing" + " ranked " + nextTo + " role on configuration topic."); throw new OntopiaRuntimeException("Error in configuration: Found more" + " than one ranked " + nextTo + " role on configuration topic."); } AssociationRoleIF rankRole = (AssociationRoleIF)rankRoles.iterator() .next(); return rankRole; } /** * Get the rank association of a given rank role (there can be at most one). * @param source A rank role of the rank association. * @return the rank association of 'source'. */ private AssociationIF getRankAssociation(AssociationRoleIF source) { // Get the rank association of the given rank role. AssociationIF rankAssociation = source.getAssociation(); if (rankAssociation == null || rankAssociation.getType() != rankAssociationType) throw new OntopiaRuntimeException("Error in configuration: Missing rank" + " association on rank role."); return rankAssociation; } /** * Get the the above/below rank role of a given rank association. * @param source The association to get the rank role from. * @param up Indicates whether to get the role moving up or down in rank. * @return the the above/below rank role played by 'source'. */ private AssociationRoleIF getRankRole(AssociationIF source, boolean up) { TopicIF targetRoleType = up ? rankAboveRoleType : rankBelowRoleType; // Get the above/below rank role of the given association. Collection rankRoles = source.getRolesByType(targetRoleType); if (rankRoles.size() > 1) { String nextTo = up ? "above" : "below"; throw new OntopiaRuntimeException("Error in configuration: Every rank" + " association must have exactly one rank-" + nextTo + "-role."); } AssociationRoleIF belowRole = (AssociationRoleIF)rankRoles.iterator() .next(); return belowRole; } /** * Add source to the end of the ranking list. * @param source The topic to be added to the end of the ranking list. */ public void rankLast(TopicIF source) { // NOTE: The rank is a circuit, with rankTopic ranked aboved the topic // with the highest rank and below the topic with the lowest rank. // Get the rank-below role played by 'rankTopic'. AssociationRoleIF role = getRankRole(rankTopic, true); // Let 'source' take the place of 'rankTopic' as the lowest ranked. role.setPlayer(source); // Now rank 'rankTopic' below 'source' to maintain the circuit. AssociationIF newRankAssociation = tmConfig.builder.makeAssociation(rankAssociationType); tmConfig.builder.makeAssociationRole(newRankAssociation, rankBelowRoleType, rankTopic); tmConfig.builder.makeAssociationRole(newRankAssociation, rankAboveRoleType, source); } public void rankNextTo(TopicIF ranked, TopicIF toRank, boolean before) { // NOTE: The rank is a circuit, with rankTopic ranked aboved the topic // with the highest rank and below the topic with the lowest rank. // Get the rank-role on the side where 'toRank' should be inserted. AssociationRoleIF nextToRole = getRankRole(ranked, before); // Attach 'toRank' to to the topic next to 'ranked'. nextToRole.setPlayer(toRank); // Now attach 'toRank' to 'ranked'. AssociationIF newRankAssociation = tmConfig.builder.makeAssociation(rankAssociationType); tmConfig.builder.makeAssociationRole(newRankAssociation, rankAboveRoleType, before ? toRank : ranked); tmConfig.builder.makeAssociationRole(newRankAssociation, rankBelowRoleType, before ? ranked : toRank); } public boolean isRanked(TopicIF configTopic) { return getConfiguredTopicTypeRank().contains(configTopic); } public List getConfiguredTopicTypeRank() { ArrayList retVal = new ArrayList(); // Start with the topic ranked just below the rank topic. TopicIF currentTopic = getNeighbourRanked(getRankTopic(), false); // Process each ranked topic in turn until the rank topic is reached (again) while (currentTopic != rankTopic) { retVal.add(currentTopic); currentTopic = getNeighbourRanked(currentTopic, false); } return retVal; } public List getRankedTopicTypes(Collection realTopics) { // Map config topics to real topics. Map configToRealTopics = new HashMap(); Iterator realTopicsIt = realTopics.iterator(); while (realTopicsIt.hasNext()) { TopicIF currentTopic = (TopicIF)realTopicsIt.next(); configToRealTopics.put(tmConfig.getConfigTopic(currentTopic), currentTopic); } List retVal = new ArrayList(); // Start with the topic ranked just below the rank topic. TopicIF currentTopic = getNeighbourRanked(getRankTopic(), false); // Process each ranked topic in turn until the rank topic is reached (again) while (currentTopic != rankTopic) { if (currentTopic == defaultTypePrecedenceTopic) { retVal.add(defaultTypePrecedenceTopic); } else { TopicIF rankedTopic = (TopicIF)configToRealTopics.get(currentTopic); if (rankedTopic != null) retVal.add(rankedTopic); } currentTopic = getNeighbourRanked(currentTopic, false); } return retVal; } public void augmentTopicTypeRank(Collection topicTypes) { List configuredRank = getConfiguredTopicTypeRank(); TopicIF afterDefault = getNeighbourRanked(defaultTypePrecedenceTopic, false); Iterator topicTypesIt = topicTypes.iterator(); while (topicTypesIt.hasNext()) { TopicIF currentTopic = (TopicIF)topicTypesIt.next(); TopicIF configTopic = tmConfig.getConfigTopic(currentTopic); if (!configuredRank.contains(configTopic)) { configuredRank.add(configTopic); // Add configTopic to the end of the ranking list. rankNextTo(afterDefault, configTopic, true); } } } public void changeRank(TopicIF realTopic, boolean up) { TopicIF topic = tmConfig.getConfigTopic(realTopic); // Get the relevant roles played by 'topic' and 'swappedTopic', i.e. the // topic is should be swapped with. AssociationRoleIF outerRole = getRankRole(topic, !up); AssociationRoleIF innerRole = getRankRole(topic, up); AssociationIF association = getRankAssociation(innerRole); AssociationRoleIF newInnerRole = getRankRole(association, up); TopicIF swappedTopic = newInnerRole.getPlayer(); AssociationRoleIF newOuterRole = getRankRole(swappedTopic, up); // Replace 'topic's position by 'swappedTopic'. outerRole.setPlayer(swappedTopic); innerRole.setPlayer(swappedTopic); // Replace 'swappedTopic's position by 'topic'. newOuterRole.setPlayer(topic); newInnerRole.setPlayer(topic); } public TopicIF highestRankedType(List realTypes) { // If the topic is untyped, no type is highest in rank. if (realTypes.isEmpty()) return null; // Sort the real topic types. Collections.sort(realTypes, topicComparator ); // Get the topics with a rank in order of rank. List rankedTypes = getRankedTopicTypes(realTypes); // If there are no ranked topics, choose the first of the real topics. if (rankedTypes.isEmpty()) return (TopicIF)realTypes.iterator().next(); TopicIF firstRanked = (TopicIF)rankedTypes.get(0); if (firstRanked != defaultTypePrecedenceTopic) return firstRanked; // For default type, search 'realTypes' for the first unranked type. Iterator realTypesIt = realTypes.iterator(); while (realTypesIt.hasNext()) { TopicIF currentType = (TopicIF)realTypesIt.next(); if (!isRanked(tmConfig.getConfigTopic(currentType))) return currentType; } // If no unranked type was found, return the ranked type below the default return (TopicIF)rankedTypes.get(1); } private TopicIF getRankTopic() { return rankTopic; } public TopicIF getDefaultTypePrecedenceTopic() { return defaultTypePrecedenceTopic; } }