/* * #! * Ontopoly Editor * #- * 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 ontopoly.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import ontopoly.utils.OntopolyModelUtils; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.index.ClassInstanceIndexIF; import net.ontopia.topicmaps.query.core.QueryResultIF; import net.ontopia.topicmaps.query.utils.RowMapperIF; import net.ontopia.topicmaps.utils.ObjectIdComparator; import net.ontopia.utils.ObjectUtils; /** * Represents an association type. */ public class AssociationType extends AbstractTypingTopic { /** * Creates a new AssociationType object. */ public AssociationType(TopicIF currTopic, TopicMap tm) { super(currTopic, tm); } @Override public LocatorIF getLocatorIF() { return PSI.ON_ASSOCIATION_TYPE; } public boolean equals(Object obj) { if (!(obj instanceof AssociationType)) return false; AssociationType other = (AssociationType) obj; return (getTopicIF().equals(other.getTopicIF())); } /** * Indicates whether the association type is symmetric. */ public boolean isSymmetric() { TopicMap tm = getTopicMap(); TopicIF aType = OntopolyModelUtils.getTopicIF(tm, PSI.ON_IS_SYMMETRIC); TopicIF rType = OntopolyModelUtils.getTopicIF(tm, PSI.ON_ASSOCIATION_TYPE); TopicIF player = getTopicIF(); return OntopolyModelUtils.isUnaryPlayer(tm, aType, player, rType); } /** * Tests whether this association type is hierarchical. * * @return true if this is association type is hierarchical. */ public boolean isHierarchical() { TopicIF hierarchicalRelationType = OntopolyModelUtils.getTopicIF(getTopicMap(), PSI.TECH_HIERARCHICAL_RELATION_TYPE); return getTopicIF().getTypes().contains(hierarchicalRelationType); } @Override public Collection<RoleField> getDeclaredByFields() { String query = "select $AF, $RF, $RT from " + "on:has-association-type(%AT% : on:association-type, $AF : on:association-field), " + "on:has-association-field($AF : on:association-field, $RF : on:role-field), " + "on:has-role-type($RF : on:role-field, $RT : on:role-type)?"; Map<String,TopicIF> params = Collections.singletonMap("AT", getTopicIF()); QueryMapper<RoleField> qm = getTopicMap().newQueryMapper(RoleField.class); return qm.queryForList(query, new RowMapperIF<RoleField>() { public RoleField mapRow(QueryResultIF result, int rowno) { TopicIF associationFieldTopic = (TopicIF)result.getValue(0); TopicIF roleFieldTopic = (TopicIF)result.getValue(1); TopicIF roleType = (TopicIF)result.getValue(2); return new RoleField(roleFieldTopic, getTopicMap(), new RoleType(roleType, getTopicMap()), new AssociationField(associationFieldTopic, getTopicMap())); } }, params); } /** * Returns all role types that have been declared for this association type. * @return list of role types */ public List<RoleType> getDeclaredRoleTypes() { List<RoleType> result = new ArrayList<RoleType>(); AssociationField associationField = null; Collection<RoleField> roleFields = this.getDeclaredByFields(); Iterator<RoleField> iter = roleFields.iterator(); while (iter.hasNext()) { RoleField roleField = iter.next(); if (associationField == null) associationField = roleField.getAssociationField(); else if (!associationField.equals(roleField.getAssociationField())) continue; result.add(roleField.getRoleType()); } // duplicate single role type if association type is symmetric if (result.size() == 1 && isSymmetric()) result.add(result.get(0)); return result; } /** * Returns a collection of lists that contain the role type combinations that have been used in actual associations. The RoleTypes are sorted by object id. * * @return Collection<List<RoleType>> */ public Collection<List<RoleType>> getUsedRoleTypeCombinations() { Collection<List<RoleType>> result = new HashSet<List<RoleType>>(); TopicIF associationType = getTopicIF(); ClassInstanceIndexIF cindex = (ClassInstanceIndexIF)associationType.getTopicMap().getIndex("net.ontopia.topicmaps.core.index.ClassInstanceIndexIF"); Iterator<AssociationIF> iter = cindex.getAssociations(associationType).iterator(); List<RoleType> tuple = new ArrayList<RoleType>(); while (iter.hasNext()) { AssociationIF assoc = iter.next(); Iterator<AssociationRoleIF> riter = assoc.getRoles().iterator(); while (riter.hasNext()) { AssociationRoleIF role = riter.next(); tuple.add(new RoleType(role.getType(), getTopicMap())); } Collections.sort(tuple, new Comparator<RoleType>() { public int compare(RoleType o1, RoleType o2) { return ObjectIdComparator.INSTANCE.compare(o1.getTopicIF(), o2.getTopicIF()); } }); if (result.contains(tuple)) { tuple.clear(); } else { result.add(tuple); tuple = new ArrayList<RoleType>(); } } return result; } /** * Transforms associations from the role types of the given form to the new one as given. * @param roleTypesFrom list of role types that should match existing associations * @param roleTypesTo list of role types to which the associations should be changed */ public void transformInstances(List<RoleType> roleTypesFrom, List<RoleType> roleTypesTo) { int size = roleTypesFrom.size(); if (size != roleTypesTo.size()) throw new RuntimeException("Incompatible role type sets: sizes are different"); TopicIF associationType = getTopicIF(); ClassInstanceIndexIF cindex = (ClassInstanceIndexIF)associationType.getTopicMap().getIndex("net.ontopia.topicmaps.core.index.ClassInstanceIndexIF"); Iterator<AssociationIF> iter = cindex.getAssociations(associationType).iterator(); AssociationRoleIF[] roleMatches = new AssociationRoleIF[size]; // for each association while (iter.hasNext()) { AssociationIF assoc = iter.next(); Collection<AssociationRoleIF> roles = assoc.getRoles(); if (roles.size() != roleTypesFrom.size()) continue; boolean match = true; Arrays.fill(roleMatches, null); Iterator<AssociationRoleIF> riter = roles.iterator(); while (riter.hasNext()) { AssociationRoleIF role = riter.next(); int matchIndex = -1; TopicIF roleType = role.getType(); for (int i=0; i < size; i++) { if (roleMatches[i] == null) { RoleType fromType = roleTypesFrom.get(i); if (fromType.getTopicIF().equals(roleType)) { matchIndex = i; roleMatches[i] = role; } } } if (matchIndex == -1) { match = false; break; } } if (match) { for (int i=0; i < size; i++) { AssociationRoleIF role = roleMatches[i]; RoleType fromType = roleTypesFrom.get(i); RoleType toType = roleTypesTo.get(i); if (role.getType().equals(fromType.getTopicIF())) { if (!role.getType().equals(toType.getTopicIF())) role.setType(toType.getTopicIF()); } } } } } static class RoleFieldComparator implements Comparator<RoleField> { private static final RoleFieldComparator INSTANCE = new RoleFieldComparator(); private RoleFieldComparator() { super(); } public static RoleFieldComparator getInstance() { return INSTANCE; } public int compare(RoleField rf1, RoleField rf2) { return ObjectUtils.compare(rf1.getFieldName(), rf2.getFieldName()); } } }