/*
* #!
* 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.Comparator;
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.AssociationIF;
import net.ontopia.topicmaps.core.AssociationRoleIF;
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.utils.ObjectUtils;
import ontopoly.utils.OntopolyModelUtils;
import ontopoly.utils.Ordering;
import ontopoly.utils.TopicComparator;
/**
* Represents a role field.
*/
public class RoleField extends FieldDefinition {
private AssociationField associationField;
private RoleType roleType;
public RoleField(TopicIF topic, TopicMap tm) {
this(topic, tm, null, null);
}
public RoleField(TopicIF topic, TopicMap tm, RoleType roleType, AssociationField associationField) {
super(topic, tm);
this.associationField = associationField;
this.roleType = roleType;
}
@Override
public int getFieldType() {
return FIELD_TYPE_ROLE;
}
@Override
public String getFieldName() {
String name = getTopicMap().getTopicName(getTopicIF(), null);
if (name != null)
return name;
AssociationType atype = getAssociationType();
RoleType rtype = getRoleType();
return (atype == null ? "" : atype.getName()) + " (" + (rtype == null ? "" : rtype.getName()) + ")";
}
@Override
public LocatorIF getLocator() {
return PSI.ON_ROLE_FIELD;
}
public boolean equals(Object obj) {
if (!(obj instanceof RoleField))
return false;
RoleField other = (RoleField)obj;
return (getTopicIF().equals(other.getTopicIF()));
}
public boolean isSortable() {
TopicMap tm = getTopicMap();
TopicIF aType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "is-sortable-field");
TopicIF rType = OntopolyModelUtils.getTopicIF(tm, PSI.ON, "field-definition");
TopicIF player = getTopicIF();
return OntopolyModelUtils.isUnaryPlayer(tm, aType, player, rType);
}
public EditMode getEditMode() {
TopicIF editModeIf = OntopolyModelUtils.findBinaryPlayer(getTopicMap(),
PSI.ON_USE_EDIT_MODE, getTopicIF(), PSI.ON_FIELD_DEFINITION, PSI.ON_EDIT_MODE);
return (editModeIf == null ? EditMode.getDefaultEditMode(getTopicMap()) : new EditMode(editModeIf, getTopicMap()));
}
public CreateAction getCreateAction() {
TopicIF createActionIf = OntopolyModelUtils.findBinaryPlayer(getTopicMap(),
PSI.ON_USE_CREATE_ACTION, getTopicIF(), PSI.ON_FIELD_DEFINITION, PSI.ON_CREATE_ACTION);
return (createActionIf == null ? CreateAction.getDefaultCreateAction(getTopicMap()) : new CreateAction(createActionIf, getTopicMap()));
}
/**
* Gets the association type.
*
* @return the association type.
*/
public AssociationType getAssociationType() {
AssociationField afield = getAssociationField();
return (afield == null ? null : getAssociationField().getAssociationType());
}
/**
* Gets the role type.
*
* @return the role type.
*/
public RoleType getRoleType() {
if (roleType == null) {
TopicIF roleTypeIf = OntopolyModelUtils.findBinaryPlayer(getTopicMap(),
PSI.ON_HAS_ROLE_TYPE, getTopicIF(), PSI.ON_ROLE_FIELD, PSI.ON_ROLE_TYPE);
this.roleType = (roleTypeIf == null ? null : new RoleType(roleTypeIf, getTopicMap()));
}
return roleType;
}
public AssociationField getAssociationField() {
if (associationField == null) {
TopicIF associationFieldIf = OntopolyModelUtils.findBinaryPlayer(getTopicMap(),
PSI.ON_HAS_ASSOCIATION_FIELD, getTopicIF(), PSI.ON_ROLE_FIELD, PSI.ON_ASSOCIATION_FIELD);
this.associationField = (associationFieldIf == null ? null : new AssociationField(associationFieldIf, getTopicMap()));
}
return associationField;
}
/**
* Gets the other RoleField objects this object's association type topic takes part in.
*
* @return the other RoleField objects this object's association type topic takes part in.
*/
public Collection<RoleField> getFieldsForOtherRoles() {
AssociationField afield = getAssociationField();
Collection<RoleField> fields = afield.getFieldsForRoles();
List<RoleField> ofields = new ArrayList<RoleField>(fields);
ofields.remove(this);
return ofields;
}
/**
* Gets the interface control assigned for this association field. If no interface control object is assigned, the
* method will return the default interface control, which is drop-down-list.
*
* @return the interface control assigned to this association field.
*/
public InterfaceControl getInterfaceControl() {
TopicIF interfaceControlIf = OntopolyModelUtils.findBinaryPlayer(getTopicMap(),
PSI.ON_USE_INTERFACE_CONTROL, getTopicIF(), PSI.ON_FIELD_DEFINITION, PSI.ON_INTERFACE_CONTROL);
return interfaceControlIf == null ? InterfaceControl.getDefaultInterfaceControl(getTopicMap()) : new InterfaceControl(interfaceControlIf, getTopicMap());
}
/**
* Gets the topic types that have been declared as valid and which
* may play the other roles in this association type.
*
* @return the topic types which may play the other roles in this association type.
*/
public Collection<TopicType> getDeclaredPlayerTypes() {
String query = "select $ttype from on:has-field(%FD% : on:field-definition, $ttype : on:field-owner)?";
Map<String,TopicIF> params = Collections.singletonMap("FD", getTopicIF());
QueryMapper<TopicType> qm = getTopicMap().newQueryMapper(TopicType.class);
return qm.queryForList(query, params);
}
public Collection<TopicType> getAllowedPlayerTypes(Topic currentTopic) {
String query = getAllowedPlayersTypesQuery();
if (query == null) {
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) }. ";
query += "select $avtype from "
+ "on:has-field(%field% : on:field-definition, $ttype : on:field-owner), "
+ "{ $avtype = $ttype | subclasses-of($ttype, $avtype) }, "
+ "not(on:is-abstract($avtype : on:topic-type)) "
+ "order by $avtype?";
}
Map<String,TopicIF> params = new HashMap<String,TopicIF>(2);
params.put("field", getTopicIF());
if (currentTopic != null)
params.put("topic", currentTopic.getTopicIF());
QueryMapper<TopicType> qm = getTopicMap().newQueryMapper(TopicType.class);
return qm.queryForList(query, params);
}
private String getAllowedPlayersQuery() {
TopicIF topicIf = getTopicIF();
TopicIF typeIf = OntopolyModelUtils.getTopicIF(getTopicMap(), PSI.ON, "allowed-players-query");
OccurrenceIF occ = OntopolyModelUtils.findOccurrence(typeIf, topicIf);
return (occ == null ? null : occ.getValue());
}
private String getAllowedPlayersSearchQuery() {
TopicIF topicIf = getTopicIF();
TopicIF typeIf = OntopolyModelUtils.getTopicIF(getTopicMap(), PSI.ON, "allowed-players-search-query");
OccurrenceIF occ = OntopolyModelUtils.findOccurrence(typeIf, topicIf);
return (occ == null ? null : occ.getValue());
}
private String getAllowedPlayersTypesQuery() {
TopicIF topicIf = getTopicIF();
TopicIF typeIf = OntopolyModelUtils.getTopicIF(getTopicMap(), PSI.ON, "allowed-players-types-query");
OccurrenceIF occ = OntopolyModelUtils.findOccurrence(typeIf, topicIf);
return (occ == null ? null : occ.getValue());
}
public List<Topic> getAllowedPlayers() {
return getAllowedPlayers(null);
}
public List<Topic> getAllowedPlayers(Topic currentTopic) {
String query = getAllowedPlayersQuery();
if (query == null) {
query = "select $instance from " +
"on:has-field(%field% : on:field-definition, $ttype : on:field-owner), " +
"instance-of($instance, $ttype) order by $instance?";
}
Map<String,TopicIF> params = new HashMap<String,TopicIF>(2);
params.put("field", getTopicIF());
if (currentTopic != null) {
params.put("topic", currentTopic.getTopicIF());
}
QueryMapper<Topic> qm = getTopicMap().newQueryMapper(Topic.class);
List<Topic> result = qm.queryForList(query, params);
Collections.sort(result, TopicComparator.INSTANCE);
return result;
}
/**
* Search for the topics that match the given search term. Only topics of allowed
* player types are returned.
*
* @param searchTerm the search term used to search for topics.
* @return a collection of Topic objects
*/
public List<Topic> searchAllowedPlayers(String searchTerm) {
try {
String query = getAllowedPlayersSearchQuery();
if (query == null)
query = "select $player, $score from "
+ "on:has-field(%field% : on:field-definition, $ttype : on:field-owner), "
+ "instance-of($player, $ttype), "
+ "topic-name($player, $tn), value-like($tn, %search%, $score) "
+ "order by $score desc, $player?";
Map<String,Object> params = new HashMap<String,Object>(2);
params.put("field", getTopicIF());
params.put("search", searchTerm);
QueryMapper<TopicIF> qm = getTopicMap().newQueryMapperNoWrap();
Collection<TopicIF> rows = qm.queryForList(query, params);
Iterator<TopicIF> it = rows.iterator();
List<Topic> results = new ArrayList<Topic>(rows.size());
Collection<TopicIF> duplicateChecks = new HashSet<TopicIF>(rows.size());
while (it.hasNext()) {
TopicIF topic = it.next();
if (duplicateChecks.contains(topic))
continue; // avoid duplicates
results.add(new Topic(topic, getTopicMap()));
duplicateChecks.add(topic);
}
return results;
} catch (Exception e) {
return Collections.emptyList();
}
}
/**
* Gets the instance topics on the other side of an association an instance topic takes part in.
*
* @param topic the instance topic that takes part in the association.
* @return the instance topics on the other side of an association an instance topic takes part in.
*/
@Override
public List<ValueIF> getValues(Topic topic) {
Collection<AssociationRoleIF> roles = getRoles(topic);
List<ValueIF> result = new ArrayList<ValueIF>(roles.size());
Iterator<AssociationRoleIF> iter = roles.iterator();
while (iter.hasNext()) {
AssociationRoleIF role = iter.next();
ValueIF value = createValue(this, role);
if (value != null)
result.add(value);
}
return result;
}
private Collection<AssociationRoleIF> getRoles(Topic topic) {
AssociationType atype = getAssociationType();
if (atype == null) return Collections.emptySet();
TopicIF associationTypeIf = atype.getTopicIF();
RoleType rtype = getRoleType();
if (rtype == null) return Collections.emptySet();
TopicIF roleTypeIf = rtype.getTopicIF();
TopicIF playerIf = topic.getTopicIF();
Collection<TopicIF> scope = Collections.emptySet();
return OntopolyModelUtils.findRoles(associationTypeIf, roleTypeIf, playerIf, scope);
}
public List<ValueIF> getOrderedValues(Topic topic, RoleField ofield) {
List<ValueIF> values = getValues(topic);
if (values.size() > 1) {
Map<Topic,OccurrenceIF> topics_occs = getValuesWithOrdering(topic);
Collections.sort(values, new MapValueComparator(topics_occs, ofield, topic));
}
return values;
}
private static class MapValueComparator implements Comparator<ValueIF> {
//! private static final String DEFAULT_ORDER_VALUE = "999999999";
private static final String DEFAULT_ORDER_VALUE = null; // sorts before "000000000"
private Map<Topic, OccurrenceIF> entries;
private RoleField ofield;
private Topic oplayer;
MapValueComparator(Map<Topic,OccurrenceIF> entries, RoleField ofield, Topic oplayer) {
this.entries = entries;
this.ofield = ofield;
this.oplayer = oplayer;
}
public int compare(ValueIF v1, ValueIF v2) {
try {
Topic p1 = v1.getPlayer(ofield, oplayer);
Topic p2 = v2.getPlayer(ofield, oplayer);
OccurrenceIF oc1 = entries.get(p1);
OccurrenceIF oc2 = entries.get(p2);
Comparable<String> c1 = (oc1 == null ? DEFAULT_ORDER_VALUE : oc1.getValue());
Comparable<String> c2 = (oc2 == null ? DEFAULT_ORDER_VALUE : oc2.getValue());
return ObjectUtils.compare(c1, c2);
} catch (Exception e) {
// should not fail when comparing. bergen kommune has had an issue where this happens. we thus ignore for now.
// e.printStackTrace();
return 0;
}
}
}
private Map<Topic,OccurrenceIF> getValuesWithOrdering(Topic topic) {
TopicIF topicIf = topic.getTopicIF();
TopicIF typeIf = OntopolyModelUtils.getTopicIF(topic.getTopicMap(), PSI.ON, "field-value-order");
LocatorIF datatype = DataTypes.TYPE_STRING;
TopicIF fieldDefinitionIf = getTopicIF();
Map<Topic,OccurrenceIF> topics_occs = new HashMap<Topic,OccurrenceIF>();
Iterator<OccurrenceIF> iter = OntopolyModelUtils.findOccurrences(typeIf, topicIf, datatype).iterator();
while (iter.hasNext()) {
OccurrenceIF occ = iter.next();
Collection<TopicIF> scope = occ.getScope();
if (scope.size() == 2 && scope.contains(fieldDefinitionIf)) { // note: this is value ordering
Iterator<TopicIF> siter = scope.iterator();
while (siter.hasNext()) {
TopicIF theme = siter.next();
if (!theme.equals(fieldDefinitionIf)) {
// FIXME: if map already contains key, we might want to delete occ
topics_occs.put(new Topic(theme, topic.getTopicMap()), occ);
break;
}
}
}
}
return topics_occs;
}
/**
* Adds an instance topic to the other side of an association an instance topic takes part in.
*
* @param topic the instance topic that takes part in the association.
* @param _value an object representing the instance topic that will be added to the other
* side of the association the instance topic (topic) takes part in.
*/
@Override
public void addValue(Topic topic, Object _value, LifeCycleListener listener) {
ValueIF value = (ValueIF) _value;
AssociationType atype = getAssociationType();
if (atype == null) return;
TopicIF atypeIf = atype.getTopicIF();
TopicIF[] rtypes = getRoleTypes(value);
TopicIF[] players = getPlayers(value);
Collection<TopicIF> scope = Collections.emptySet();
// if cardinality is 0:1 or 1:1 then clear existing values
if (getCardinality().isMaxOne()) {
// remove all existing values
ValueIF existingValue = null;
Collection<AssociationRoleIF> roles = getRoles(topic);
boolean replaceValues = roles.size() == 1;
Iterator<AssociationRoleIF> iter = roles.iterator();
while (iter.hasNext()) {
AssociationRoleIF role = iter.next();
ValueIF valueIf = createValue(this, role);
if (valueIf == null) {
continue;
} if (valueIf.equals(value)) {
existingValue = valueIf;
} else if (replaceValues) {
// issue-204: only replace values if there is just a single value
removeValue(topic, valueIf, listener);
}
}
// create new
if (existingValue == null) {
OntopolyModelUtils.makeAssociation(atypeIf, rtypes, players, scope);
}
} else {
Collection<AssociationIF> assocs = OntopolyModelUtils.findAssociations(atypeIf, rtypes, players, scope);
if (assocs.isEmpty()) {
// create new
OntopolyModelUtils.makeAssociation(atypeIf, rtypes, players, scope);
} else {
// remove all except the first one
Iterator<AssociationIF> iter = assocs.iterator();
iter.next();
while (iter.hasNext()) {
AssociationIF assoc = iter.next();
assoc.remove();
}
}
}
if (listener != null) listener.onAfterAdd(topic, this, value);
}
// protected void clear(FieldInstance fieldInstance, LifeCycleListener listener) {
// Collection roles = getRoles(fieldInstance.getInstance());
// Iterator iter = roles.iterator();
// while (iter.hasNext()) {
// AssociationRoleIF role = (AssociationRoleIF)iter.next();
// ValueIF valueIf = createValue(this, role);
// removeValue(fieldInstance, valueIf, listener);
// }
// }
/**
* Removes an instance topic from the other side of an association an instance topic takes part in.
*
* @param _value an object representing the instance topic that will be removed from the other
* side of the association the instance topic (topic) takes part in.
*/
@Override
public void removeValue(Topic topic, Object _value, LifeCycleListener listener) {
ValueIF value = (ValueIF) _value;
AssociationType atype = getAssociationType();
if (atype == null) return;
TopicIF atypeIf = atype.getTopicIF();
TopicIF[] rtypes = getRoleTypes(value);
TopicIF[] players = getPlayers(value);
if (listener != null) listener.onBeforeRemove(topic, this, value);
Collection<TopicIF> scope = Collections.emptySet();
Collection<AssociationIF> assocs = OntopolyModelUtils.findAssociations(atypeIf, rtypes, players, scope);
if (!assocs.isEmpty()) {
// remove all the matching
Iterator<AssociationIF> iter = assocs.iterator();
while (iter.hasNext()) {
AssociationIF assoc = iter.next();
assoc.remove();
}
}
// TODO: consider removing field value order also
}
/**
* Factory method for creating a ValueIF object, which represent an instance topic on one side of an association.
*
* @param roleField the role field containing the association type and the role type representing another side of the association.
* @param role the role type on the side of the association that the instance topic is going to be created.
* @return the ValueIF object that represent an instance topic on one side of an association. Will return null if role does not match role field definition.
*/
private static ValueIF createValue(RoleField roleField, AssociationRoleIF role) {
Collection<RoleField> fields = roleField.getAssociationField().getFieldsForRoles();
int fieldCount = fields.size();
TopicMap topicMap = roleField.getTopicMap();
AssociationIF assoc = role.getAssociation();
// ignore roles where the arity does not match
Collection<AssociationRoleIF> aroles = assoc.getRoles();
if (fieldCount != aroles.size())
return null;
ValueIF value = createValue(fieldCount);
value.addPlayer(roleField, new Topic(role.getPlayer(), roleField.getTopicMap()));
Object[] roles = aroles.toArray();
Collection<AssociationRoleIF> matched = new HashSet<AssociationRoleIF>(roles.length);
matched.add(role);
int selfMatch = 0;
Iterator<RoleField> iter = fields.iterator();
while (iter.hasNext()) {
RoleField ofield = iter.next();
// only match your own field once
if (ofield.equals(roleField)) {
if (++selfMatch == 1)
continue;
}
RoleType ortype = ofield.getRoleType();
if (ortype == null) return null;
boolean match = false;
for (int i = 0; i < roles.length; i++) {
AssociationRoleIF orole = (AssociationRoleIF) roles[i];
if (matched.contains(orole))
continue;
if (ObjectUtils.equals(orole.getType(), ortype.getTopicIF())) {
matched.add(orole);
value.addPlayer(ofield, new Topic(orole.getPlayer(), topicMap));
match = true;
}
}
if (!match)
return null;
}
return value;
}
/**
* Factory method for creating a ValueIF object, which represent an instance topic on one side of an association.
*
* @param arity the number of players that the association value should have.
* @return the ValueIF object that represent an instance topic on one side of an association.
*/
public static ValueIF createValue(int arity) {
return new Value(arity);
}
/**
* Interface. This interface is implemented by the Value class.
*/
public static interface ValueIF {
public int getArity();
public RoleField[] getRoleFields();
public Topic[] getPlayers();
public void addPlayer(RoleField roleField, Topic player);
public Topic getPlayer(RoleField roleField, Topic oplayer);
}
/**
* Static inner class containing a Map object, which connects
* instance topics to associations.
*/
private static class Value implements RoleField.ValueIF {
int offset;
RoleField[] roleFields;
Topic[] players;
Value(int arity) {
this.roleFields = new RoleField[arity];
this.players = new Topic[arity];
}
public int getArity() {
return roleFields.length;
}
public RoleField[] getRoleFields() {
return roleFields;
}
public Topic[] getPlayers() {
return players;
}
public void addPlayer(RoleField roleField, Topic player) {
roleFields[offset] = roleField;
players[offset] = player;
offset++;
}
public Topic getPlayer(RoleField ofield, Topic oPlayer) {
// NOTE: all this logic is here to cater for symmetric associations
Topic xPlayer = null;
for (int i=0; i < roleFields.length; i++) {
RoleField rf = roleFields[i];
if (rf.equals(ofield)) {
Topic player = players[i];
if (ObjectUtils.different(player, oPlayer))
return player;
else
xPlayer = oPlayer;
}
}
if (xPlayer == null)
throw new RuntimeException("Could not find player for RoleField: " + ofield + " (" + oPlayer + ")");
else
return xPlayer;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ValueIF(");
sb.append(getArity());
sb.append(": ");
for (int i=0; i < roleFields.length; i++) {
if (i > 0) sb.append(", ");
if (roleFields[i] == null)
sb.append("null");
else {
RoleType rtype = roleFields[i].getRoleType();
sb.append((rtype == null ? null : rtype.getTopicIF()));
}
sb.append(":");
if (players[i] == null)
sb.append("null");
else
sb.append(players[i].getTopicIF());
}
sb.append(")");
return sb.toString();
}
}
private TopicIF[] getRoleTypes(ValueIF value) {
RoleField[] roleFields = value.getRoleFields();
int arity = value.getArity();
TopicIF[] rtypes = new TopicIF[arity];
for (int i=0; i < arity; i++) {
rtypes[i] = roleFields[i].getRoleType().getTopicIF();
}
return rtypes;
}
private TopicIF[] getPlayers(ValueIF value) {
Topic[] players = value.getPlayers();
int arity = value.getArity();
TopicIF[] topics = new TopicIF[arity];
for (int i=0; i < arity; i++) {
topics[i] = players[i].getTopicIF();
}
return topics;
}
private Collection<Topic> getValues(Topic instance, RoleField ofield) {
Collection<ValueIF> values = getValues(instance);
Collection<Topic> result = new HashSet<Topic>(values.size());
Iterator<ValueIF> iter = values.iterator();
while (iter.hasNext()) {
ValueIF rfv = iter.next();
Topic player = rfv.getPlayer(ofield, instance);
result.add(player);
}
return result;
}
/**
* Change field value order so that the first value is ordered directly after the second value.
**/
public void moveAfter(Topic instance, RoleField ofield, RoleField.ValueIF rfv1, RoleField.ValueIF rfv2) {
Topic p1 = rfv1.getPlayer(ofield, instance);
Topic p2 = rfv2.getPlayer(ofield, instance);
TopicIF typeIf = OntopolyModelUtils.getTopicIF(instance.getTopicMap(), PSI.ON, "field-value-order");
LocatorIF datatype = DataTypes.TYPE_STRING;
TopicIF fieldDefinitionIf = getTopicIF();
TopicIF topicIf = instance.getTopicIF();
TopicIF p1topic = p1.getTopicIF();
TopicIF p2topic = p2.getTopicIF();
Collection<Topic> alltopics = getValues(instance, ofield);
Map<Topic,OccurrenceIF> topics_occs = getValuesWithOrdering(instance);
List<OccurrenceIF> occs = new ArrayList<OccurrenceIF>(topics_occs.values());
Collections.sort(occs, new Comparator<OccurrenceIF>() {
public int compare(OccurrenceIF occ1, OccurrenceIF occ2) {
return ObjectUtils.compare(occ1.getValue(), occ2.getValue());
}
});
TopicMapBuilderIF builder = topicIf.getTopicMap().getBuilder();
OccurrenceIF maxOcc = (occs.isEmpty() ? null : occs.get(occs.size()-1));
int fieldOrderMax = (maxOcc == null ? 0 : Ordering.stringToOrder(maxOcc.getValue()));
// make sure this value has an order value
OccurrenceIF p1occ = null;
OccurrenceIF p2occ = topics_occs.get(p2);
OccurrenceIF next_occ = null;
int fieldOrderP2;
int nextOrder = Ordering.MAX_ORDER;
if (p2occ == null) {
fieldOrderP2 = (fieldOrderMax == 0 ? 0 : fieldOrderMax + Ordering.ORDER_INCREMENTS);
p2occ = builder.makeOccurrence(topicIf, typeIf, Ordering.orderToString(fieldOrderP2), datatype);
p2occ.addTheme(fieldDefinitionIf);
p2occ.addTheme(p2topic);
} else {
fieldOrderP2 = Ordering.stringToOrder(p2occ.getValue());
// find occurrence after p2occ
int indexP2occ = occs.indexOf(p2occ);
if (indexP2occ < (occs.size()-1))
next_occ = occs.get(indexP2occ+1);
if (next_occ != null) {
// if next then average this and next field orders
int fieldOrderNext = Ordering.stringToOrder(next_occ.getValue());
nextOrder = (fieldOrderP2 + fieldOrderNext)/2;
if (nextOrder != fieldOrderP2) {
p1occ = topics_occs.get(p1);
if (p1occ != null) {
p1occ.setValue(Ordering.orderToString(nextOrder));
} else {
p1occ = builder.makeOccurrence(topicIf, typeIf, Ordering.orderToString(nextOrder), datatype);
p1occ.addTheme(fieldDefinitionIf);
p1occ.addTheme(p1topic);
}
}
}
}
if (nextOrder == Ordering.MAX_ORDER)
nextOrder = fieldOrderP2;
if (p1occ == null) {
nextOrder += Ordering.ORDER_INCREMENTS;
p1occ = topics_occs.get(p1);
if (p1occ != null) {
p1occ.setValue(Ordering.orderToString(nextOrder));
} else {
p1occ = builder.makeOccurrence(topicIf, typeIf, Ordering.orderToString(nextOrder), datatype);
p1occ.addTheme(fieldDefinitionIf);
p1occ.addTheme(p1topic);
}
// we need to reshuffle all existing orders after p2
int indexP2occ = occs.indexOf(p2occ);
if (indexP2occ > 0) {
for (int i=indexP2occ+1; i < occs.size(); i++) {
OccurrenceIF occ = occs.get(i);
nextOrder += Ordering.ORDER_INCREMENTS;
occ.setValue(Ordering.orderToString(nextOrder));
}
}
}
// assign ordering to all topics with no existing ordering
alltopics.remove(p1);
alltopics.remove(p2);
Iterator<Topic> aiter = alltopics.iterator();
while (aiter.hasNext()) {
Topic atopic = aiter.next();
if (!topics_occs.containsKey(atopic)) {
nextOrder += Ordering.ORDER_INCREMENTS;
OccurrenceIF occ = builder.makeOccurrence(topicIf, typeIf, Ordering.orderToString(nextOrder), datatype);
occ.addTheme(fieldDefinitionIf);
occ.addTheme(atopic.getTopicIF());
}
}
}
public Collection<RoleField> getOtherRoleFields() {
AssociationField associationField = getAssociationField();
List<RoleField> roleFields = associationField.getFieldsForRoles();
List<RoleField> result = new ArrayList<RoleField>(roleFields.size());
for (RoleField roleField : roleFields) {
if (!roleField.equals(this)) {
result.add(roleField);
}
}
return result;
}
}