/*
* #!
* 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.utils.CopyUtils;
import net.ontopia.topicmaps.utils.TypeHierarchyUtils;
import net.ontopia.utils.OntopiaRuntimeException;
import ontopoly.utils.TopicComparator;
/**
* INTERNAL: Common superclass for all topics, like instances, association
* types, topic types, role types, etc. FIXME: Should there be another subtype
* for isHidden, isReadOnly etc?
*/
public class Topic {
private TopicIF topicIF;
private TopicMap tm;
private String cachedName;
/**
* Constructor.
*
* @param topicIF the TopicIF object associated with this topic.
* @param tm the TopicMap this topic belongs to.
*/
public Topic(TopicIF topicIF, TopicMap tm) {
this.topicIF = topicIF;
this.tm = tm;
}
// ------------------------------------------------------------------------
// ABSTRACT TOPIC
/**
* Gets the id of this topic.
*
* @return the id of this topic.
*/
public String getId() {
return getTopicIF().getObjectId();
}
/**
* Gets the topicIF object of this topic.
*
* @return the topicIF object of this topic.
*/
public TopicIF getTopicIF() {
return topicIF;
}
/**
* Gets the topicMap this topic belongs to.
*
* @return the topicMap this topic belongs to.
*/
public TopicMap getTopicMap() {
return tm;
}
/**
* Gets the unscoped name of the topic.
*
* @return the unscoped name of the topic or null if no name has been set.
*/
public String getName() {
if (cachedName == null)
cachedName = tm.getTopicName(getTopicIF(), null);
return cachedName;
}
/**
* Tests whether this topic is a topic map.
*
* @return true if this is a topic map.
*/
public boolean isTopicMap() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_TOPIC_MAP);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is a topic type.
*
* @return true if this is a topic type.
*/
public boolean isTopicType() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_TOPIC_TYPE);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is a name type.
*
* @return true if this is a name type.
*/
public boolean isNameType() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_NAME_TYPE);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is an occurrence type.
*
* @return true if this is an occurrence type.
*/
public boolean isOccurrenceType() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_OCCURRENCE_TYPE);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is an association type.
*
* @return true if this is an association type.
*/
public boolean isAssociationType() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_ASSOCIATION_TYPE);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is a role type.
*
* @return true if this is a role type.
*/
public boolean isRoleType() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_ROLE_TYPE);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is a field definition or an association field.
*
* @return true if this is a role type.
*/
public boolean isFieldDefinition() {
TopicMapIF topicMap = topicIF.getTopicMap();
TopicIF associationField = topicMap.getTopicBySubjectIdentifier(PSI.ON_ASSOCIATION_FIELD);
TopicIF identityField = topicMap.getTopicBySubjectIdentifier(PSI.ON_IDENTITY_FIELD);
TopicIF nameField = topicMap.getTopicBySubjectIdentifier(PSI.ON_NAME_FIELD);
TopicIF occurrenceField = topicMap.getTopicBySubjectIdentifier(PSI.ON_OCCURRENCE_FIELD);
TopicIF roleField = topicMap.getTopicBySubjectIdentifier(PSI.ON_ROLE_FIELD);
for (TopicIF topicType : topicIF.getTypes()) {
if (topicType.equals(associationField) ||
topicType.equals(identityField) ||
topicType.equals(nameField) ||
topicType.equals(occurrenceField) ||
topicType.equals(roleField)) {
return true;
}
}
return false;
}
/**
* Tests whether this topic is a system topic type.
*
* @return true if this is a system topic type.
*/
public boolean isSystemTopic() {
Collection<TopicIF> types = topicIF.getTypes();
TopicIF systemType = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_SYSTEM_TOPIC);
if (types.contains(systemType)) return true;
TopicIF publicSystemType = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_PUBLIC_SYSTEM_TOPIC);
if (types.contains(publicSystemType)) return true;
return false;
}
public boolean isPrivateSystemTopic() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_SYSTEM_TOPIC);
return topicIF.getTypes().contains(type);
}
public boolean isPublicSystemTopic() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_PUBLIC_SYSTEM_TOPIC);
return topicIF.getTypes().contains(type);
}
/**
* Tests whether this topic is an instance of an ontology type, i.e. an instance of topic type,
* name type, occurrence type, association type or role type.
*
* @return true if this is an instance of an ontology type.
*/
public boolean isOntologyTopic() {
return isTopicType() || isNameType() || isOccurrenceType() || isAssociationType() || isRoleType();
}
/**
* Tests whether this topic is an ontology type, i.e. topic type,
* name type, occurrence type, association type or role type.
*
* @return true if this is an ontology type.
*/
public boolean isOntologyType() {
TopicIF type = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_TYPE);
return topicIF.getTypes().contains(type);
}
// ------------------------------------------------------------------------
// INSTANCE
/**
* Given the topic type, find the subtype that is the specific type
* of this topic. The topic type given is usually a super type of
* the specific type. If null is returned then the topic type is not
* a super type of this topic.
* @param topicType the super type of this topic
* @return the most specific type of this type, or null if there is none.
*/
public TopicType getMostSpecificTopicType(TopicType topicType) {
String query = "supertype-of($SUB, $SUP) :-"
+ " { xtm:superclass-subclass($SUB : xtm:subclass, $SUP : xtm:superclass) |"
+ " xtm:superclass-subclass($SUB : xtm:subclass, $X : xtm:superclass), "
+ " supertype-of($X, $SUP) }. "
+ "select $DTYPE from "
+ "$TOPIC = %topic%, $XTYPE = %topicType%, "
+ "direct-instance-of($TOPIC, $DTYPE), "
+ "{ $XTYPE = $DTYPE | supertype-of($DTYPE, $XTYPE) } ?";
Map<String,TopicIF> params = new HashMap<String,TopicIF>(2);
params.put("topic", getTopicIF());
params.put("topicType", topicType.getTopicIF());
QueryMapper<TopicType> qm = getTopicMap().newQueryMapper(TopicType.class);
return qm.queryForObject(query, params);
}
/**
* Returns the topic types of which this topic is a direct instance.
*/
public List<TopicType> getTopicTypes() {
TopicIF topicIF = getTopicIF();
Collection<TopicIF> topicTypes = topicIF.getTypes();
int size = topicTypes.size();
if (size == 0)
return Collections.emptyList();
List<TopicType> result = new ArrayList<TopicType>(size);
TopicIF topicTypeTopic = topicIF.getTopicMap().getTopicBySubjectIdentifier(PSI.ON_TOPIC_TYPE);
Iterator<TopicIF> iter = topicTypes.iterator();
while (iter.hasNext()) {
TopicIF topicType = iter.next();
if (topicType.getTypes().contains(topicTypeTopic))
result.add(new TopicType(topicType, getTopicMap()));
}
Collections.sort(result, TopicComparator.INSTANCE);
return result;
}
/**
* Adds the topic type to the list of topic types that topic is a
* direct instance of.
*/
public void addTopicType(TopicType type) {
if (type == null)
throw new OntopiaRuntimeException("The input parameter is null");
getTopicIF().addType(type.getTopicIF());
}
/**
* Removes the topic type from the list of topic types that topic is
* a direct instance of.
*/
public void removeTopicType(TopicType type) {
if (type == null)
throw new OntopiaRuntimeException("The input parameter is null");
getTopicIF().removeType(type.getTopicIF());
}
public List<FieldInstance> getFieldInstances(TopicType topicType) {
return getFieldInstances(topicType, null);
}
public List<FieldInstance> getFieldInstances(TopicType topicType, FieldsView fieldsView) {
List<FieldAssignment> fieldAssignments = topicType.getFieldAssignments(fieldsView);
List<FieldInstance> fieldInstances = new ArrayList<FieldInstance>(fieldAssignments.size());
Iterator<FieldAssignment> it = fieldAssignments.iterator();
while (it.hasNext()) {
FieldAssignment fa = it.next();
fieldInstances.add(new FieldInstance(this, fa));
}
return fieldInstances;
}
/**
* Removes the topic from the topic map.
* @param listener listener that gets call back from the deleting this topic, and any dependencies.p
*/
public void remove(LifeCycleListener listener) {
if (listener != null) listener.onBeforeDelete(this);
topicIF.remove();
}
public Collection<Topic> getDependentObjects() {
Collection<Topic> result = new HashSet<Topic>();
findDependentObjects(result);
return result;
}
/**
* Find all objects that are dependent on this topic and that should
* be removed if this topic is removed. At the moment a dependent
* object is an object that is associated to this topic through a
* role field that has on:create-only-mode enabled. The dependencies
* are transitive.
*/
protected void findDependentObjects(Collection<Topic> alreadyKnownDependentObjects) {
Collection<Topic> newPlayers = new HashSet<Topic>();
Iterator<TopicType> titer = getTopicTypes().iterator();
while (titer.hasNext()) {
TopicType topicType = titer.next();
List<FieldAssignment> fieldAssignments = topicType.getFieldAssignments();
Iterator<FieldAssignment> fiter = fieldAssignments.iterator();
while (fiter.hasNext()) {
FieldAssignment fieldAssignment = fiter.next();
FieldDefinition fieldDefinition = fieldAssignment.getFieldDefinition();
if (fieldDefinition.getFieldType() != FieldDefinition.FIELD_TYPE_ROLE)
continue;
RoleField roleField = (RoleField)fieldDefinition;
if (roleField.getEditMode().isOwnedValues()) {
// field contains dependent objects
Collection<RoleField> otherFields = roleField.getFieldsForOtherRoles();
if (otherFields.size() != 1) continue;
RoleField ofield = otherFields.iterator().next();
Collection<RoleField.ValueIF> values = roleField.getValues(this);
Iterator<RoleField.ValueIF> viter = values.iterator();
while (viter.hasNext()) {
RoleField.ValueIF value = viter.next();
Topic oplayer = value.getPlayer(ofield, this);
// track newly found objects
if (!alreadyKnownDependentObjects.contains(oplayer))
newPlayers.add(oplayer);
}
}
}
}
alreadyKnownDependentObjects.addAll(newPlayers);
Iterator<Topic> niter = newPlayers.iterator();
while (niter.hasNext()) {
Topic nplayer = niter.next();
nplayer.findDependentObjects(alreadyKnownDependentObjects);
}
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Topic)) {
return false;
}
Topic t = (Topic) o;
return t.getTopicIF().equals(getTopicIF());
}
@Override
public int hashCode() {
return getTopicIF().hashCode();
}
public String toString() {
return super.toString() + "[" + getTopicIF() + "]";
}
public Topic copyCharacteristics() {
return new Topic(CopyUtils.copyCharacteristics(getTopicIF()), getTopicMap());
}
private static final TypeHierarchyUtils thutils = new TypeHierarchyUtils();
public boolean isInstanceOf(Topic type) {
return thutils.isInstanceOf(getTopicIF(), type.getTopicIF());
}
public boolean isInstanceOf(LocatorIF psi) {
TopicIF topic = getTopicIF().getTopicMap().getTopicBySubjectIdentifier(psi);
return thutils.isInstanceOf(getTopicIF(), topic);
}
}