/* * #! * 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.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.OccurrenceIF; import net.ontopia.topicmaps.core.ReifiableIF; 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.utils.StringUtils; import net.ontopia.utils.OntopiaRuntimeException; /** * PUBLIC: Utilities for generating keys from complex topic map * objects. The keys from two different objects in the same topic map * will be equal if the two objects are equal according to the * equality rules of the TMDM. */ public class KeyGenerator { /** * PUBLIC: Makes a key for an occurrence. The key is made up of * a scope key, a type key, and a data key. * * @return string containing key */ public static String makeOccurrenceKey(OccurrenceIF occ) { return makeScopeKey(occ) + "$" + makeTypedKey(occ) + makeDataKey(occ); } /** * PUBLIC: Makes a key for an occurrence, as it would look in * another topic map. * * @since 5.1.3 * @return string containing key */ public static String makeOccurrenceKey(OccurrenceIF occ, TopicMapIF othertm) { return makeScopeKey(occ, othertm) + "$" + makeTypedKey(occ, othertm) + makeDataKey(occ); } /** * PUBLIC: Makes a key for a topic name. The key is made up of * a scope key and a value key. * * @return string containing key */ public static String makeTopicNameKey(TopicNameIF bn) { return makeScopeKey(bn) + "$" + makeTypedKey(bn) + "$$" + bn.getValue(); } /** * PUBLIC: Makes a key for a topic name, as it would look in another * topic map. * * @since 5.1.3 * @return string containing key */ public static String makeTopicNameKey(TopicNameIF bn, TopicMapIF othertm) { return makeScopeKey(bn, othertm) + "$" + makeTypedKey(bn, othertm) + "$$" + bn.getValue(); } /** * PUBLIC: Makes a key for a variant name. The key is made up of * a scope key and a value/locator key. * * @since 1.2.0 * @return string containing key */ public static String makeVariantKey(VariantNameIF vn) { return makeScopeKey(vn) + makeDataKey(vn); } /** * PUBLIC: Makes a key for an association. The key is made up from * the type and for each role its type and player. * @param assoc The association to make a key for * @return string containing key */ public static String makeAssociationKey(AssociationIF assoc) { StringBuilder sb = new StringBuilder(); // asssociation type key fragment sb.append(makeTypedKey(assoc)); sb.append("$"); sb.append(makeScopeKey(assoc)); sb.append("$"); List<AssociationRoleIF> roles = new ArrayList<AssociationRoleIF>(assoc.getRoles()); String[] rolekeys = new String[roles.size()]; for (int i = 0; i < rolekeys.length; i++) rolekeys[i] = makeAssociationRoleKey(roles.get(i)); Arrays.sort(rolekeys); sb.append(StringUtils.join(rolekeys, "$")); return sb.toString(); } /** * PUBLIC: Makes a key for an association, but does not include * the player of the given role. The key is made up from the type * and for each role its type and player (except the given role for * which only the role type is included). This method is used to * create a signature for the association without taking one of the * roles into account. * @param assoc The association to make a key for * @param role The association role whose role player is to be left * out of the key. * @return string containing key * @since 3.4.2 */ public static String makeAssociationKey(AssociationIF assoc, AssociationRoleIF role) { StringBuilder sb = new StringBuilder(); // asssociation type key fragment sb.append(makeTypedKey(assoc)); sb.append("$"); sb.append(makeScopeKey(assoc)); sb.append("$"); List<AssociationRoleIF> roles = new ArrayList<AssociationRoleIF>(assoc.getRoles()); roles.remove(role); String[] rolekeys = new String[roles.size()]; for (int i = 0; i < rolekeys.length; i++) rolekeys[i] = makeAssociationRoleKey(roles.get(i)); sb.append(makeTypedKey(role)); sb.append("$"); Arrays.sort(rolekeys); sb.append(StringUtils.join(rolekeys, "$")); return sb.toString(); } /** * PUBLIC: Makes a key for an association, as it would look in another * topic map. * @since 5.1.3 */ public static String makeAssociationKey(AssociationIF assoc, TopicMapIF othertm) { StringBuilder sb = new StringBuilder(); // asssociation type key fragment sb.append(makeTypedKey(assoc, othertm)); sb.append("$"); sb.append(makeScopeKey(assoc, othertm)); sb.append("$"); Collection<AssociationRoleIF> roles = new ArrayList<AssociationRoleIF>(assoc.getRoles()); String[] rolekeys = new String[roles.size()]; int i = 0; for (AssociationRoleIF role : roles) rolekeys[i++] = makeAssociationRoleKey(role, othertm); Arrays.sort(rolekeys); sb.append(StringUtils.join(rolekeys, "$")); return sb.toString(); } /** * PUBLIC: Makes a key for an association role. The key is made up * of the role and the player. * @since 1.2.0 * @param role The association role. * @return The key. */ public static String makeAssociationRoleKey(AssociationRoleIF role) { return makeTypedKey(role) + ":" + role.getPlayer().getObjectId(); } /** * PUBLIC: Makes a key for an association role, as it would look in * another topic map. * @since 5.1.3 */ public static String makeAssociationRoleKey(AssociationRoleIF role, TopicMapIF othertm) { TopicIF otherplayer = MergeUtils.findTopic(othertm, role.getPlayer()); return makeTypedKey(role, othertm) + ":" + otherplayer.getObjectId(); } /** * PUBLIC: Makes a key for any reifiable object, using the other * methods in this class. * @since 5.1.0 */ public static String makeKey(ReifiableIF object) { if (object instanceof TopicNameIF) return makeTopicNameKey((TopicNameIF) object); else if (object instanceof OccurrenceIF) return makeOccurrenceKey((OccurrenceIF) object); else if (object instanceof AssociationIF) return makeAssociationKey((AssociationIF) object); else if (object instanceof AssociationRoleIF) return makeAssociationRoleKey((AssociationRoleIF) object); else if (object instanceof VariantNameIF) return makeVariantKey((VariantNameIF) object); else throw new OntopiaRuntimeException("Cannot make key for: " + object); } /** * PUBLIC: Makes a key for any reifiable object as it would look * like were the object in another topic map. Useful for checking if * a given object exists in another topic map. * * @param object The object to make a key for. * @param topicmap The topic map in which to interpret the key. * * @since 5.1.3 */ public static String makeKey(ReifiableIF object, TopicMapIF topicmap) { if (object instanceof TopicNameIF) return makeTopicNameKey((TopicNameIF) object, topicmap); else if (object instanceof OccurrenceIF) return makeOccurrenceKey((OccurrenceIF) object, topicmap); else if (object instanceof AssociationIF) return makeAssociationKey((AssociationIF) object, topicmap); else if (object instanceof AssociationRoleIF) return makeAssociationRoleKey((AssociationRoleIF) object, topicmap); else throw new OntopiaRuntimeException("Cannot make key for: " + object); } // --- Helper methods // used by TopicMapSynchronizer protected static String makeTopicKey(TopicIF topic) { if (topic == null) return ""; else return topic.getObjectId(); } protected static String makeTypedKey(TypedIF typed) { return typed.getType().getObjectId(); } protected static String makeTypedKey(TypedIF typed, TopicMapIF othertm) { TopicIF othertype = MergeUtils.findTopic(othertm, typed.getType()); return othertype.getObjectId(); } protected static String makeScopeKey(ScopedIF scoped) { return makeScopeKey(scoped.getScope()); } protected static String makeScopeKey(ScopedIF scoped, TopicMapIF othertm) { return makeScopeKey(scoped.getScope(), othertm); } protected static String makeScopeKey(Collection<TopicIF> scope) { Iterator<TopicIF> it = scope.iterator(); String[] ids = new String[scope.size()]; int ix = 0; while (it.hasNext()) { TopicIF theme = it.next(); ids[ix++] = theme.getObjectId(); } Arrays.sort(ids); return StringUtils.join(ids, " "); } protected static String makeScopeKey(Collection<TopicIF> scope, TopicMapIF othertm) { Iterator<TopicIF> it = scope.iterator(); String[] ids = new String[scope.size()]; int ix = 0; while (it.hasNext()) { TopicIF theme = it.next(); TopicIF othertheme = MergeUtils.findTopic(othertm, theme); if (othertheme == null) throw new OntopiaRuntimeException("No topic corresponding to: " + theme); ids[ix++] = othertheme.getObjectId(); } Arrays.sort(ids); return StringUtils.join(ids, " "); } protected static String makeDataKey(OccurrenceIF occ) { return "$$" + occ.getValue() + "$" + occ.getDataType(); } protected static String makeDataKey(VariantNameIF variant) { return "$$" + variant.getValue() + "$" + variant.getDataType(); } }