/* * #! * 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.impl.rdbms; import java.util.Collection; import java.util.Collections; import net.ontopia.persistence.proxy.IdentityIF; import net.ontopia.persistence.proxy.IdentityNotFoundException; import net.ontopia.persistence.proxy.TransactionIF; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.ConstraintViolationException; import net.ontopia.topicmaps.core.CrossTopicMapException; import net.ontopia.topicmaps.core.DuplicateReificationException; import net.ontopia.topicmaps.core.ReifiableIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.impl.utils.DeletionUtils; import net.ontopia.topicmaps.impl.utils.ObjectStrings; import net.ontopia.utils.CompactHashSet; /** * INTERNAL: The rdbms association implementation. */ public class Association extends TMObject implements AssociationIF { // --------------------------------------------------------------------------- // Persistent property declarations // --------------------------------------------------------------------------- protected static final int LF_scope = 2; protected static final int LF_type = 3; protected static final int LF_roles = 4; protected static final int LF_reifier = 5; protected static final String[] fields = {"sources", "topicmap", "scope", "type", "roles", "reifier"}; public void detach() { detachCollectionField(LF_sources); detachField(LF_topicmap); detachField(LF_reifier); detachCollectionField(LF_scope); detachField(LF_type); detachCollectionField(LF_roles); } // --------------------------------------------------------------------------- // Data members // --------------------------------------------------------------------------- static final String CLASS_INDICATOR = "A"; public Association() { } public Association(TransactionIF txn) { super(txn); } // --------------------------------------------------------------------------- // PersistentIF implementation // --------------------------------------------------------------------------- public int _p_getFieldCount() { return fields.length; } // --------------------------------------------------------------------------- // TMObjectIF implementation // --------------------------------------------------------------------------- public String getClassIndicator() { return CLASS_INDICATOR; } public String getObjectId() { return (id == null ? null : CLASS_INDICATOR + id.getKey(0)); } // --------------------------------------------------------------------------- // AssociationIF implementation // --------------------------------------------------------------------------- /** * INTERNAL: Sets the topic map that the object belongs to. [parent] */ void setTopicMap(TopicMap topicmap) { // Notify transaction transactionChanged(topicmap); valueChanged(LF_topicmap, topicmap, true); // Inform association roles for (AssociationRole role : this.<AssociationRole>loadCollectionField(LF_roles)) { role.setTopicMap(topicmap); } } public Collection<TopicIF> getRoleTypes() { Collection<TopicIF> result = new CompactHashSet<TopicIF>(); for (AssociationRoleIF role : this.<AssociationRoleIF>loadCollectionField(LF_roles)) { TopicIF type = role.getType(); if (type != null) result.add(role.getType()); } return result; } public Collection<AssociationRoleIF> getRolesByType(TopicIF roletype) { if (roletype == null) throw new NullPointerException("Role type must not be null."); CrossTopicMapException.check(roletype, this); Collection<AssociationRoleIF> result = new CompactHashSet<AssociationRoleIF>(); for (AssociationRoleIF role : this.<AssociationRoleIF>loadCollectionField(LF_roles)) if (role.getType() == roletype) result.add(role); return result; } public Collection<AssociationRoleIF> getRoles() { try { return this.<AssociationRoleIF>loadCollectionField(LF_roles); } catch (IdentityNotFoundException e) { // association has been deleted by somebody else, so return empty set return Collections.EMPTY_SET; } } void addRole(AssociationRoleIF assoc_role) { if (assoc_role == null) throw new NullPointerException("null is not a valid argument."); // Check to see if association role is already a member of this association if (assoc_role.getAssociation() == this) return; // Check if used elsewhere. if (assoc_role.getAssociation() != null) throw new ConstraintViolationException("Moving objects is not allowed."); // Notify listeners fireEvent(AssociationIF.EVENT_ADD_ROLE, assoc_role, null); // Set association property ((AssociationRole)assoc_role).setAssociation(this); // Add association role to list of association roles valueAdded(LF_roles, assoc_role, false); // Make sure role is added to player's list Topic player = (Topic) assoc_role.getPlayer(); if (player != null && getTopicMap() != null) player.addRole(assoc_role); } void removeRole(AssociationRoleIF assoc_role) { if (assoc_role == null) throw new NullPointerException("null is not a valid argument."); // Check to see if association role is not a member of this association if (assoc_role.getAssociation() != this) return; // Notify listeners fireEvent(AssociationIF.EVENT_REMOVE_ROLE, null, assoc_role); // Unset association property ((AssociationRole)assoc_role).setAssociation(null); // Remove role from list of roles valueRemoved(LF_roles, assoc_role, false); // Removing role from player's list of roles Topic player = (Topic) assoc_role.getPlayer(); if (player != null && getTopicMap() != null) { // Notify listeners ((AssociationRole)assoc_role).fireEvent(AssociationRoleIF.EVENT_SET_PLAYER, player, null); player.removeRole(assoc_role); } } public void remove() { TopicMap topicmap = (TopicMap)getTopicMap(); if (topicmap != null) { DeletionUtils.removeDependencies(this); topicmap.removeAssociation(this); } } // --------------------------------------------------------------------------- // ScopedIF implementation // --------------------------------------------------------------------------- public Collection<TopicIF> getScope() { return this.<TopicIF>loadCollectionField(LF_scope); } public void addTheme(TopicIF theme) { if (theme == null) throw new NullPointerException("null is not a valid argument."); CrossTopicMapException.check(theme, this); // Notify listeners fireEvent(AssociationIF.EVENT_ADD_THEME, theme, null); // Notify transaction valueAdded(LF_scope, theme, true); } public void removeTheme(TopicIF theme) { if (theme == null) throw new NullPointerException("null is not a valid argument."); CrossTopicMapException.check(theme, this); // Notify listeners fireEvent(AssociationIF.EVENT_REMOVE_THEME, null, theme); // Notify transaction valueRemoved(LF_scope, theme, true); } // --------------------------------------------------------------------------- // TypedIF implementation // --------------------------------------------------------------------------- public TopicIF getType() { try { return this.<TopicIF>loadField(LF_type); } catch (IdentityNotFoundException e) { // association has been deleted by somebody else, so return null return null; } } public void setType(TopicIF type) { if (type == null) throw new NullPointerException("Association type must not be null."); CrossTopicMapException.check(type, this); // Notify listeners fireEvent(AssociationIF.EVENT_SET_TYPE, type, getType()); // Notify transaction valueChanged(LF_type, type, true); } // --------------------------------------------------------------------------- // ReifiableIF implementation // --------------------------------------------------------------------------- public TopicIF getReifier() { try { return this.<TopicIF>loadField(LF_reifier); } catch (IdentityNotFoundException e) { // association has been deleted by somebody else, so return null return null; } } public void setReifier(TopicIF _reifier) { if (_reifier != null) CrossTopicMapException.check(_reifier, this); if (DuplicateReificationException.check(this, _reifier)) { return; } // Notify listeners Topic reifier = (Topic)_reifier; Topic oldReifier = (Topic)getReifier(); fireEvent(ReifiableIF.EVENT_SET_REIFIER, reifier, oldReifier); valueChanged(LF_reifier, reifier, true); if (oldReifier != null) oldReifier.setReified(null); if (reifier != null) reifier.setReified(this); } // --------------------------------------------------------------------------- // Misc. methods // --------------------------------------------------------------------------- public String toString() { return ObjectStrings.toString("rdbms.Association", (AssociationIF) this); } @Override public void syncAfterMerge(IdentityIF source, IdentityIF target) { super.syncFieldsAfterMerge(source, target, LF_type, LF_reifier, LF_scope); } }