/*
* #!
* 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.basic;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
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.UniqueSet;
/**
* INTERNAL: The basic association implementation.
*/
public class Association extends TMObject implements AssociationIF {
static final long serialVersionUID = -8986947932370957132L;
protected TopicIF reifier;
protected TopicIF type;
protected UniqueSet<TopicIF> scope;
protected Set<AssociationRoleIF> roles;
protected Association(TopicMap tm) {
super(tm);
roles = topicmap.cfactory.makeSmallSet();
}
// -----------------------------------------------------------------------------
// AssociationIF implementation
// -----------------------------------------------------------------------------
/**
* INTERNAL: Sets the topic map that the object belongs to. [parent]
*/
void setTopicMap(TopicMap parent) {
// (De)reference pooled sets
if (scope != null) {
if (parent == null)
topicmap.setpool.dereference(scope);
else
scope = topicmap.setpool.get(scope);
}
// Set parent
this.parent = parent;
}
public Collection<TopicIF> getRoleTypes() {
Collection<TopicIF> result = topicmap.cfactory.makeSmallSet();
synchronized (roles) {
Iterator<AssociationRoleIF> iter = roles.iterator();
while (iter.hasNext()) {
AssociationRoleIF role = iter.next();
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 = topicmap.cfactory.makeSmallSet();
synchronized (roles) {
Iterator<AssociationRoleIF> iter = roles.iterator();
while (iter.hasNext()) {
AssociationRoleIF role = iter.next();
if (role.getType() == roletype)
result.add(role);
}
}
return result;
}
public Collection<AssociationRoleIF> getRoles() {
return Collections.unmodifiableSet(roles);
}
void addRole(AssociationRoleIF _assoc_role) {
AssociationRole assoc_role = (AssociationRole)_assoc_role;
if (assoc_role == null)
throw new NullPointerException(MSG_NULL_ARGUMENT);
// Check to see if association role is already a member of this association
if (assoc_role.parent == this)
return;
// Check if used elsewhere.
if (assoc_role.parent != null)
throw new ConstraintViolationException("Moving objects is not allowed.");
// Notify listeners
fireEvent(AssociationIF.EVENT_ADD_ROLE, assoc_role, null);
// Set association property
assoc_role.setAssociation(this);
// Add association role to list of association roles
roles.add(assoc_role);
// Make sure role is added to player's list
Topic player = (Topic) assoc_role.getPlayer();
if (player != null && parent != null)
player.addRole(assoc_role);
}
void removeRole(AssociationRoleIF _assoc_role) {
AssociationRole assoc_role = (AssociationRole)_assoc_role;
if (assoc_role == null)
throw new NullPointerException(MSG_NULL_ARGUMENT);
// Check to see if association role is not a member of this association
if (assoc_role.parent != this)
return;
// Notify listeners
fireEvent(AssociationIF.EVENT_REMOVE_ROLE, null, assoc_role);
// Remove role from list of roles
roles.remove(assoc_role);
// Removing role from player's list of roles
Topic player = (Topic) assoc_role.getPlayer();
if (player != null && parent != null)
player.removeRole(assoc_role);
// Unset association property
assoc_role.setAssociation(null);
}
public void remove() {
if (topicmap != null) {
DeletionUtils.removeDependencies(this);
topicmap.removeAssociation(this);
}
}
// -----------------------------------------------------------------------------
// ScopedIF implementation
// -----------------------------------------------------------------------------
public Collection<TopicIF> getScope() {
// Return scope defined on this object
return (scope == null ? Collections.<TopicIF>emptyList() : scope);
}
public void addTheme(TopicIF theme) {
if (theme == null)
throw new NullPointerException(MSG_NULL_ARGUMENT);
CrossTopicMapException.check(theme, this);
// Notify listeners
fireEvent(AssociationIF.EVENT_ADD_THEME, theme, null);
// Add theme to scope
if (scope == null) {
scope = topicmap.setpool.get(Collections.<TopicIF>emptySet());
}
scope = topicmap.setpool.add(scope, theme, true);
}
public void removeTheme(TopicIF theme) {
if (theme == null)
throw new NullPointerException(MSG_NULL_ARGUMENT);
CrossTopicMapException.check(theme, this);
// Notify listeners
fireEvent(AssociationIF.EVENT_REMOVE_THEME, null, theme);
// Remove theme from scope
if (scope == null)
return;
scope = topicmap.setpool.remove(scope, theme, true);
}
// -----------------------------------------------------------------------------
// TypedIF implementation
// -----------------------------------------------------------------------------
public TopicIF getType() {
return type;
}
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());
this.type = type;
}
// -----------------------------------------------------------------------------
// ReifiableIF implementation
// -----------------------------------------------------------------------------
public TopicIF getReifier() {
return reifier;
}
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);
this.reifier = reifier;
if (oldReifier != null) oldReifier.setReified(null);
if (reifier != null) reifier.setReified(this);
}
// -----------------------------------------------------------------------------
// Misc. methods
// -----------------------------------------------------------------------------
public String toString() {
return ObjectStrings.toString("basic.Association", (AssociationIF)this);
}
}