/*
* #!
* 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 java.util.Set;
import ontopoly.utils.FieldAssignmentOrderComparator;
import ontopoly.utils.OntopolyModelUtils;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.topicmaps.core.AssociationIF;
import net.ontopia.topicmaps.core.DataTypes;
import net.ontopia.topicmaps.core.OccurrenceIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapBuilderIF;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import net.ontopia.topicmaps.query.utils.RowMapperIF;
import net.ontopia.utils.StringUtils;
/**
* INTERNAL: Represents a topic type.
*/
public class TopicType extends AbstractTypingTopic {
public TopicType(TopicIF currTopic, TopicMap tm) {
super(currTopic, tm);
}
/**
* Tests whether this topic type is abstract.
*
* @return true if this topic type is abstract.
*/
public boolean isAbstract() {
return isTrueAssociation("is-abstract", "topic-type");
}
/**
* Tests whether this topic type has a large instance set.
*/
public boolean isLargeInstanceSet() {
return isTrueAssociation("has-large-instance-set", "topic-type");
}
/**
* Tests whether this topic type has a hierarchy-forming association
* type attached to it.
*/
public boolean hasHierarchy() {
// on:forms-hierarchy-for($TTYPE : on:topic-type, $ATYPE : on:association-type)
TopicMap tm = getTopicMap();
TopicIF aType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "forms-hierarchy-for");
TopicIF rType1 = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "topic-type");
TopicIF rType2 = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "association-type");
TopicIF topicIF = getTopicIF();
return OntopolyModelUtils.hasBinaryAssociation(topicIF, aType, rType1,
rType2);
}
private boolean isTrueAssociation(String atype, String rtype) {
TopicMap tm = getTopicMap();
TopicIF aType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, atype);
TopicIF rType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, rtype);
TopicIF topicIF = getTopicIF();
AssociationIF assoc = OntopolyModelUtils.findUnaryAssociation(tm, aType, topicIF, rType);
return (assoc != null);
}
/**
* Gets the direct subtypes of this type.
*
* @return A Collection of TopicType objects.
*/
public Collection<TopicType> getDirectSubTypes() {
String query = "select $SUB from xtm:superclass-subclass($SUB : xtm:subclass, %topic% : xtm:superclass) order by $SUB?";
Map<String,TopicIF> params = Collections.singletonMap("topic", getTopicIF());
QueryMapper<TopicType> qm = getTopicMap().newQueryMapper(TopicType.class);
return qm.queryForList(query, params);
}
/**
* Gets the all subtypes (direct and indirect) of this type.
*
* @return A Collection of TopicType objects.
*/
public Collection<TopicType> getAllSubTypes() {
String query = "subclasses-of($SUP, $SUB) :- { "
+ "xtm:superclass-subclass($SUP : xtm:superclass, $SUB : xtm:subclass) | "
+ "xtm:superclass-subclass($SUP : xtm:superclass, $MID : xtm:subclass), "
+ "subclasses-of($MID, $SUB) }. " + "subclasses-of(%topic%, $SUB)?";
Map<String,TopicIF> params = Collections.singletonMap("topic", getTopicIF());
QueryMapper<TopicType> qm = getTopicMap().newQueryMapper(TopicType.class);
return qm.queryForList(query, params);
}
/**
* Returns the supertype of this type, or null if there is none.
*/
public TopicType getSuperType() {
String query = "xtm:superclass-subclass(%topic% : xtm:subclass, $SUP : xtm:superclass)?";
Map<String,TopicIF> params = Collections.singletonMap("topic", getTopicIF());
QueryMapper<TopicType> qm = getTopicMap().newQueryMapper(TopicType.class);
return qm.queryForObject(query, params);
}
public FieldAssignment addField(FieldDefinition fieldDefinition) {
TopicMap tm = getTopicMap();
final TopicIF HAS_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-field");
final TopicIF HAS_CARDINALITY = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-cardinality");
final TopicIF FIELD_DEFINITION = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "field-definition");
final TopicIF FIELD_OWNER = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "field-owner");
final TopicIF CARDINALITY = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "cardinality");
TopicIF fieldDefinitionTopic = fieldDefinition.getTopicIF();
TopicIF topicTypeTopic = getTopicIF();
fieldOrderMaintainance(this);
// on:has-field($TT : on:field-owner, $FD : on:field-definition)
OntopolyModelUtils.makeBinaryAssociation(HAS_FIELD,
topicTypeTopic, FIELD_OWNER,
fieldDefinitionTopic, FIELD_DEFINITION);
// on:has-cardinality($TT : on:field-owner, $FD : on:field-definition, $C : on:cardinality)
OntopolyModelUtils.makeTernaryAssociation(HAS_CARDINALITY,
topicTypeTopic, FIELD_OWNER,
fieldDefinitionTopic, FIELD_DEFINITION,
Cardinality.getDefaultCardinality(fieldDefinition).getTopicIF(), CARDINALITY);
// Add field-order occurrence for this topictype and all it's subtypes.
addFieldOrder(this, fieldDefinition);
return new FieldAssignment(this, this, fieldDefinition);
}
public void removeField(FieldDefinition fieldDefinition) {
TopicMap tm = getTopicMap();
final TopicIF HAS_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON_HAS_FIELD);
final TopicIF HAS_CARDINALITY = OntopolyModelUtils.getTopicIF(tm, PSI.ON_HAS_CARDINALITY);
final TopicIF FIELD_DEFINITION = OntopolyModelUtils.getTopicIF(tm, PSI.ON_FIELD_DEFINITION);
final TopicIF CARDINALITY = OntopolyModelUtils.getTopicIF(tm, PSI.ON_CARDINALITY);
final TopicIF FIELD_OWNER = OntopolyModelUtils.getTopicIF(tm, PSI.ON_FIELD_OWNER);
TopicIF fieldDefinitionTopic = fieldDefinition.getTopicIF();
TopicIF topicTypeTopic = getTopicIF();
// find and remove has-cardinality association
AssociationIF associationIF = OntopolyModelUtils.findTernaryAssociation(tm, HAS_CARDINALITY,
topicTypeTopic, FIELD_OWNER,
fieldDefinitionTopic, FIELD_DEFINITION,
Cardinality.getDefaultCardinality(fieldDefinition).getTopicIF(), CARDINALITY);
if (associationIF != null)
associationIF.remove();
// find and remove has-field association
associationIF = OntopolyModelUtils.findBinaryAssociation(tm, HAS_FIELD,
topicTypeTopic, FIELD_OWNER,
fieldDefinitionTopic, FIELD_DEFINITION);
if (associationIF != null)
associationIF.remove();
// See if one of the supertypes have also defined this field. If some of
// the supertypes has defined
// this field, don't remove the field-order occurrence.
boolean removeFieldOrder = true;
Iterator<FieldAssignment> it = getFieldAssignments().iterator();
while (it.hasNext()) {
FieldAssignment fa = it.next();
if (fa.getFieldDefinition().equals(fieldDefinition)) {
removeFieldOrder = false;
break;
}
}
if (removeFieldOrder) {
// Remove field-order occurrence from this topictype and all it's
// subtypes which have defined it.
removeFieldOrder(this, fieldDefinition);
}
}
public NameType createNameType() {
TopicMap tm = getTopicMap();
TopicMapBuilderIF builder = tm.getTopicMapIF().getBuilder();
// create name field
TopicIF nameFieldType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "name-field");
TopicIF nameFieldTopic = builder.makeTopic(nameFieldType);
// create name type
TopicIF nameTypeTopic = builder.makeTopic(OntopolyModelUtils.getTopicIF(tm, PSI.ON, "name-type"));
NameType nameType = new NameType(nameTypeTopic, tm);
// on:has-name-type($TT : on:name-type, $FD : on:name-field)
final TopicIF HAS_NAME_TYPE = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-name-type");
final TopicIF NAME_TYPE = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "name-type");
final TopicIF NAME_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "name-field");
OntopolyModelUtils.makeBinaryAssociation(HAS_NAME_TYPE,
nameTypeTopic, NAME_TYPE,
nameFieldTopic, NAME_FIELD);
// TODO: add default cardinality
// add field
NameField nameField = new NameField(nameFieldTopic, tm, nameType);
addField(nameField);
return nameType;
}
public QueryField createQueryField() {
TopicMap tm = getTopicMap();
TopicMapBuilderIF builder = tm.getTopicMapIF().getBuilder();
// create name field
TopicIF queryFieldType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "query-field");
TopicIF queryFieldTopic = builder.makeTopic(queryFieldType);
// add field
QueryField queryField = new QueryField(queryFieldTopic, tm);
addField(queryField);
return queryField;
}
public OccurrenceType createOccurrenceType() {
TopicMap tm = getTopicMap();
TopicMapBuilderIF builder = tm.getTopicMapIF().getBuilder();
// create occurrence field
TopicIF occurrenceFieldType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "occurrence-field");
TopicIF occurrenceFieldTopic = builder.makeTopic(occurrenceFieldType);
// create occurrence type
TopicIF occurrenceTypeTopic = builder.makeTopic(OntopolyModelUtils.getTopicIF(tm, PSI.ON, "occurrence-type"));
OccurrenceType occurrenceType = new OccurrenceType(occurrenceTypeTopic, tm);
// on:has-occurrence-type($TT : on:occurrence-type, $FD : on:occurrence-field)
final TopicIF HAS_OCCURRENCE_TYPE = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-occurrence-type");
final TopicIF OCCURRENCE_TYPE = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "occurrence-type");
final TopicIF OCCURRENCE_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "occurrence-field");
OntopolyModelUtils.makeBinaryAssociation(HAS_OCCURRENCE_TYPE,
occurrenceTypeTopic, OCCURRENCE_TYPE,
occurrenceFieldTopic, OCCURRENCE_FIELD);
// TODO: add default datatype and cardinality
// add field
OccurrenceField occurrenceField = new OccurrenceField(occurrenceFieldTopic, tm);
addField(occurrenceField);
return occurrenceType;
}
public AssociationType createAssociationType() {
TopicMap tm = getTopicMap();
TopicMapBuilderIF builder = tm.getTopicMapIF().getBuilder();
// create role field
TopicIF roleFieldType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "role-field");
TopicIF roleFieldTopic = builder.makeTopic(roleFieldType);
// create association field
TopicIF associationFieldType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "association-field");
TopicIF associationFieldTopic = builder.makeTopic(associationFieldType);
// create association type
TopicIF associationTypeTopic = builder.makeTopic(OntopolyModelUtils.getTopicIF(tm, PSI.ON, "association-type"));
AssociationType associationType = new AssociationType(associationTypeTopic, tm);
// on:has-association-type($TT : on:association-type, $FD : on:association-field)
final TopicIF HAS_ASSOCIATION_TYPE = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-association-type");
final TopicIF ASSOCIATION_TYPE = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "association-type");
final TopicIF ASSOCIATION_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "association-field");
OntopolyModelUtils.makeBinaryAssociation(HAS_ASSOCIATION_TYPE,
associationType.getTopicIF(), ASSOCIATION_TYPE,
associationFieldTopic, ASSOCIATION_FIELD);
// on:has-association-field($AF : on:association-field, $FD : on:role-field)
final TopicIF HAS_ASSOCIATION_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-association-field");
final TopicIF ROLE_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "role-field");
OntopolyModelUtils.makeBinaryAssociation(HAS_ASSOCIATION_FIELD,
roleFieldTopic, ROLE_FIELD,
associationFieldTopic, ASSOCIATION_FIELD);
// TODO: add default cardinality
// add field
RoleField roleField = new RoleField(roleFieldTopic, tm);
addField(roleField);
// create second role field
TopicIF roleFieldTopic2 = builder.makeTopic(roleFieldType);
// on:has-association-field($AF : on:association-field, $FD : on:role-field)
OntopolyModelUtils.makeBinaryAssociation(HAS_ASSOCIATION_FIELD,
roleFieldTopic2, ROLE_FIELD,
associationFieldTopic, ASSOCIATION_FIELD);
return associationType;
}
// Assures that every fieldAssignment assigned to this topic type has a
// fieldOrder
private static void fieldOrderMaintainance(TopicType tt) {
final TopicIF FIELD_ORDER = OntopolyModelUtils.getTopicIF(tt.getTopicMap(), PSI.ON, "field-order");
TopicIF topicIF = tt.getTopicIF();
List<FieldAssignment> fieldAssignments = tt.getFieldAssignments();
Iterator<FieldAssignment> it = fieldAssignments.iterator();
while (it.hasNext()) {
FieldAssignment fa = it.next();
FieldDefinition fieldDefinition = fa.getFieldDefinition();
Collection<TopicIF> scope = Collections.singleton(fieldDefinition.getTopicIF());
OccurrenceIF occurrenceIF = OntopolyModelUtils.findOccurrence(
FIELD_ORDER, topicIF, DataTypes.TYPE_STRING, scope);
if (occurrenceIF == null) {
String fieldOrderAsString;
int fieldOrder = fa.getOrder(tt);
if (fieldOrder != Integer.MAX_VALUE)
fieldOrderAsString = StringUtils.pad(fieldOrder + 1, '0', 9);
else
fieldOrderAsString = tt.getNextUnusedFieldOrder();
// create field-order occurrence
OntopolyModelUtils.makeOccurrence(FIELD_ORDER, topicIF,
fieldOrderAsString, DataTypes.TYPE_STRING, scope);
}
}
}
private static void addFieldOrder(TopicType tt, FieldDefinition fieldDefinition) {
final TopicIF FIELD_ORDER = OntopolyModelUtils.getTopicIF(tt.getTopicMap(), PSI.ON, "field-order");
TopicIF topicTypeTopic = tt.getTopicIF();
TopicIF fieldDefinitionTopic = fieldDefinition.getTopicIF();
Collection<TopicIF> scope = Collections.singleton(fieldDefinitionTopic);
// see if field-order occurrence already exist for the same field
OccurrenceIF occurrenceIF = OntopolyModelUtils.findOccurrence(FIELD_ORDER,
topicTypeTopic, DataTypes.TYPE_STRING, scope);
if (occurrenceIF != null)
return;
// create field-order occurrence
OntopolyModelUtils.makeOccurrence(FIELD_ORDER, topicTypeTopic,
tt.getNextUnusedFieldOrder(), DataTypes.TYPE_STRING, scope);
// Go through all of TopicType tt's subtypes depth-first.
Iterator<TopicType> it = tt.getDirectSubTypes().iterator();
while (it.hasNext()) {
addFieldOrder(it.next(), fieldDefinition);
}
}
private static void removeFieldOrder(TopicType tt, FieldDefinition fieldDefinition) {
// See if the same field is defined on this topic type.
TopicMap tm = tt.getTopicMap();
final TopicIF HAS_FIELD = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "has-field");
final TopicIF FIELD_DEFINITION = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "field-definition");
final TopicIF FIELD_OWNER = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "field-owner");
TopicIF topicTypeTopic = tt.getTopicIF();
TopicIF fieldDefinitionTopic = fieldDefinition.getTopicIF();
AssociationIF associationIF = OntopolyModelUtils.findBinaryAssociation(
tt.getTopicMap(), HAS_FIELD,
topicTypeTopic, FIELD_OWNER,
fieldDefinitionTopic, FIELD_DEFINITION);
// The field is defined on this topic type too, hence the field-order
// occurrence can't be removed.
if (associationIF != null)
return;
// find field-order occurrence
Collection<TopicIF> scope = Collections.singleton(fieldDefinitionTopic);
OccurrenceIF occurrenceIF = OntopolyModelUtils.findOccurrence(
OntopolyModelUtils.getTopicIF(tm, PSI.ON, "field-order"),
tt.getTopicIF(), DataTypes.TYPE_STRING, scope);
// remove field-order occurrence if it exist
if (occurrenceIF != null) {
occurrenceIF.remove();
// Go through all of TopicType tt's subtypes depth-first.
Iterator<TopicType> it = tt.getDirectSubTypes().iterator();
while (it.hasNext()) {
removeFieldOrder(it.next(), fieldDefinition);
}
}
}
/**
* Returns the FieldAssignments for this topic type. These are sorted by the
* field order field on the field types. In addition, fields are inherited
* from all ancestor types.
*
* <p>
* Note that if isSystemTopic(), the list of fields will always contain the
* default name type with the "exactly one" cardinality at the very top
*/
public List<FieldAssignment> getFieldAssignments() {
return getFieldAssignments(null);
}
public List<FieldAssignment> getFieldAssignments(FieldsView view) {
String viewClause = "";
if (view != null) {
if (view.isDefaultView())
viewClause = "{ on:field-in-view($FD : on:field-definition, on:default-fields-view : on:fields-view) | not(on:field-in-view($FD : on:field-definition, $XV : on:fields-view), $XV /= on:default-fields-view) }, ";
else
viewClause = "on:field-in-view($FD : on:field-definition, %view% : on:fields-view), ";
}
String query =
"subclasses-of($SUP, $SUB) :- { " +
" xtm:superclass-subclass($SUP : xtm:superclass, $SUB : xtm:subclass) | " +
" xtm:superclass-subclass($SUP : xtm:superclass, $MID : xtm:subclass), subclasses-of($MID, $SUB) " +
"}. " +
"field-order($T, $FA, $FO) :- " +
" { occurrence($T, $O), type($O, on:field-order), " +
" scope($O, $FA), value($O, $FO) || " +
" xtm:superclass-subclass($T : xtm:subclass, $TT : xtm:superclass), " +
" field-order($TT, $FA, $FO) }. " +
"select $TT, $FD, $FT, $FO from " +
"{ $TT = %tt% | subclasses-of($TT, %tt%) }, " +
"on:has-field($TT : on:field-owner, $FD : on:field-definition), " +
viewClause +
"direct-instance-of($FD, $FT), xtm:superclass-subclass($FT : xtm:subclass, on:field-definition : xtm:superclass), " +
"{ field-order(%tt%, $FD, $FO) }?";
Map<String,TopicIF> params;
if (view == null)
params = Collections.singletonMap("tt", getTopicIF());
else {
params = new HashMap<String,TopicIF>(2);
params.put("tt", getTopicIF());
params.put("view", view.getTopicIF());
}
QueryMapper<FieldAssignment> qm = getTopicMap().newQueryMapperNoWrap();
List<FieldAssignment> fieldAssignments = qm.queryForList(query,
new RowMapperIF<FieldAssignment>() {
public FieldAssignment mapRow(QueryResultIF result, int rowno) {
TopicIF topicType = (TopicIF)result.getValue(0);
TopicIF fieldDefinitionTopic = (TopicIF)result.getValue(1);
TopicIF fieldDefinitionType = (TopicIF)result.getValue(2);
// OPTIMIZATION: retrieving field order here so we can pass it to the constructor
String foValue = (String)result.getValue(3);
int fieldOrder = (foValue != null ? Integer.parseInt(foValue) : Integer.MAX_VALUE);
TopicMap tm = getTopicMap();
TopicType tt = new TopicType(topicType, tm);
FieldDefinition fd = findFieldDefinitionImpl(tm, fieldDefinitionTopic, fieldDefinitionType);
return new FieldAssignment(TopicType.this, tt, fd, fieldOrder);
}
}, params);
Collections.sort(fieldAssignments, FieldAssignmentOrderComparator.INSTANCE);
return fieldAssignments;
}
static FieldDefinition findFieldDefinitionImpl(TopicMap tm, TopicIF fieldDefinitionTopic, TopicIF fieldDefinitionType) {
Collection<LocatorIF> identities = fieldDefinitionType.getSubjectIdentifiers();
if (identities.contains(PSI.ON_OCCURRENCE_FIELD))
return new OccurrenceField(fieldDefinitionTopic, tm);
else if (identities.contains(PSI.ON_ROLE_FIELD))
return new RoleField(fieldDefinitionTopic, tm);
else if (identities.contains(PSI.ON_NAME_FIELD))
return new NameField(fieldDefinitionTopic, tm);
else if (identities.contains(PSI.ON_IDENTITY_FIELD))
return new IdentityField(fieldDefinitionTopic, tm);
else if (identities.contains(PSI.ON_QUERY_FIELD))
return new QueryField(fieldDefinitionTopic, tm);
else
throw new OntopolyModelRuntimeException(
"This topic's subjectIndicator address didn't match any FieldDefinition implementations: "
+ identities);
}
private String getNextUnusedFieldOrder() {
int fieldOrder = 0;
// find field-order occurrence
Collection<OccurrenceIF> fieldOrderOccurrences = OntopolyModelUtils.findOccurrences(
OntopolyModelUtils.getTopicIF(getTopicMap(), PSI.ON, "field-order"),
getTopicIF(), DataTypes.TYPE_STRING);
Iterator<OccurrenceIF> it = fieldOrderOccurrences.iterator();
while (it.hasNext()) {
OccurrenceIF occurrenceIF = it.next();
int temp = Integer.parseInt(occurrenceIF.getValue());
if (temp > fieldOrder)
fieldOrder = temp;
}
return StringUtils.pad(fieldOrder + 1, '0', 9);
}
/**
* Returns the set of all instances of this topic type.
*
* @return A collection of Topic objects.
*/
public List<Topic> getInstances() {
String query = "instance-of($instance, %topic%) order by $instance?";
Map<String,TopicIF> params = Collections.singletonMap("topic", getTopicIF());
QueryMapper<Topic> qm = getTopicMap().newQueryMapper(Topic.class);
return qm.queryForList(query, params);
}
/**
* Create a new topic instance of this topic type.
*/
public Topic createInstance(String name) {
TopicMap tm = getTopicMap();
// delegate to specific create method if known type
Collection<LocatorIF> subinds = getTopicIF().getSubjectIdentifiers();
if (subinds.contains(PSI.ON_TOPIC_TYPE))
return tm.createTopicType(name);
else if (subinds.contains(PSI.ON_ASSOCIATION_TYPE))
return tm.createAssociationType(name);
else if (subinds.contains(PSI.ON_ROLE_TYPE))
return tm.createRoleType(name);
else if (subinds.contains(PSI.ON_NAME_TYPE))
return tm.createNameType(name);
else if (subinds.contains(PSI.ON_OCCURRENCE_TYPE))
return tm.createOccurrenceType(name);
// use default create method
TopicIF topic = tm.createNamedTopic(name, getTopicIF());
return new Topic(topic, tm);
}
@Override
public LocatorIF getLocatorIF() {
return PSI.ON_TOPIC_TYPE;
}
/**
* Returns the topics that matches the given search term. Only topics of
* allowed player types are returned.
*
* @return a collection of Topic objects
*/
public List<Topic> searchAll(String searchTerm) {
String query = "select $topic, $score from "
+ "value-like($tn, %searchTerm%, $score), topic-name($topic, $tn), instance-of($topic, %topicType%) "
+ "order by $score desc, $topic?";
Map<String,Object> params = new HashMap<String,Object>();
params.put("searchTerm", searchTerm);
params.put("topicType", getTopicIF());
QueryMapper<Topic> qm = getTopicMap().newQueryMapper(Topic.class);
Collection<Topic> rows = qm.queryForList(query, params);
Iterator<Topic> it = rows.iterator();
List<Topic> results = new ArrayList<Topic>(rows.size());
try {
Set<Topic> duplicateChecks = new HashSet<Topic>(rows.size());
while (it.hasNext()) {
Topic topic = it.next();
if (duplicateChecks.contains(topic))
continue; // avoid duplicates
results.add(topic);
duplicateChecks.add(topic);
}
} catch (Exception e) {
e.printStackTrace();
}
return results;
}
@Override
public Collection<? extends FieldDefinition> getDeclaredByFields() {
return Collections.emptyList();
}
public List<FieldsView> getFieldViews(FieldsView fieldsView) {
// TODO: make it possible to override this query
String query =
"subclasses-of($SUP, $SUB) :- { " +
" xtm:superclass-subclass($SUP : xtm:superclass, $SUB : xtm:subclass) | " +
" xtm:superclass-subclass($SUP : xtm:superclass, $MID : xtm:subclass), subclasses-of($MID, $SUB) " +
"}. " +
"select $FIELDSVIEW from " +
"{ $TT = %tt% | subclasses-of($TT, %tt%) }, " +
"on:has-field($TT : on:field-owner, $FD : on:field-definition), " +
"{ on:field-in-view($FD : on:field-definition, $FV : on:fields-view)" +
", not(on:is-hidden-view($FV : on:fields-view))" +
", not(on:is-embedded-view($FV : on:fields-view))" +
" || $FV = on:default-fields-view}, coalesce($FIELDSVIEW, $FV, on:default-fields-view) order by $FIELDSVIEW?";
Map<String,TopicIF> params = new HashMap<String,TopicIF>(3);
// params.put("topic", getTopicIF());
params.put("tt", getTopicIF());
params.put("view", fieldsView.getTopicIF());
QueryMapper<FieldsView> qm = getTopicMap().newQueryMapperNoWrap();
return qm.queryForList(query,
new RowMapperIF<FieldsView>() {
public FieldsView mapRow(QueryResultIF result, int rowno) {
TopicIF viewTopic = (TopicIF)result.getValue(0);
if (viewTopic == null)
return FieldsView.getDefaultFieldsView(getTopicMap());
else
return new FieldsView(viewTopic, getTopicMap());
}
}, params);
}
}