/* * #! * 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.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import ontopoly.utils.OntopolyModelUtils; import ontopoly.utils.Ordering; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.topicmaps.core.DataTypes; import net.ontopia.topicmaps.core.OccurrenceIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.utils.ObjectUtils; /** * Represents a field as assigned to a topic type. The field itself is a * FieldDefinition, and the topic type a TopicType. This object primarily * holds the cardinality and order in the list of fields. */ public final class FieldAssignment { private FieldDefinition fieldDefinition; private TopicType topicType; private TopicType declaredTopicType; private TopicMap tm; private int cachedOrder = Integer.MAX_VALUE; /** * Creates a new field assignment object. */ public FieldAssignment(TopicType topicType, TopicType declaredTopicType, FieldDefinition fieldDefinition) { this.fieldDefinition = fieldDefinition; this.topicType = topicType; this.declaredTopicType = declaredTopicType; this.tm = topicType.getTopicMap(); } public FieldAssignment(TopicType topicType, TopicType declaredTopicType, FieldDefinition fieldDefinition, int cachedOrder) { this(topicType, declaredTopicType, fieldDefinition); this.cachedOrder = (cachedOrder == Integer.MAX_VALUE ? cachedOrder - 1 : cachedOrder); } void refresh() { this.cachedOrder = Integer.MAX_VALUE; } /** * Returns the topic type. */ public TopicType getTopicType() { return topicType; } /** * Returns the topic type. */ public TopicType getDeclaredTopicType() { return declaredTopicType; } /** * Returns the field type. */ public FieldDefinition getFieldDefinition() { return fieldDefinition; } public Cardinality getCardinality() { return getFieldDefinition().getCardinality(); } /** * Returns the ordering key of the field on this topic type. */ public int getOrder() { if (cachedOrder < Integer.MAX_VALUE) return cachedOrder; int order = getOrder(topicType); this.cachedOrder = (order == Integer.MAX_VALUE ? order - 1 : order); return cachedOrder; } /** * Returns the ordering key of the field on the topic type sent in as an * argument. */ public int getOrder(TopicType t) { String value; Map<String,Object> queryResult; TopicIF tt = t.getTopicIF(); String query = "field-order-value($tt, $f, $v) :- " + "occurrence($tt, $OCC), scope($OCC, $f), value($OCC, $v). " + "select $value, $super from " + "$tt = %tt%, " + "$f = %f%, " + "{field-order-value($tt, $f, $value)}, " + "{xtm:superclass-subclass($super : xtm:superclass , $tt : xtm:subclass) } limit 1? "; Map<String,TopicIF> params = new HashMap<String,TopicIF>(); params.put("tt", tt); params.put("f", fieldDefinition.getTopicIF()); QueryMapper<Object> qm = tm.newQueryMapperNoWrap(); while (true) { queryResult = qm.queryForMap(query, params); value = (String) queryResult.get("value"); tt = (TopicIF) queryResult.get("super"); params.put("tt", tt); return Ordering.stringToOrder(value); } } private void setOrder(int order) { setOrder(getTopicType().getTopicMap(), getTopicType().getTopicIF(), getFieldDefinition().getTopicIF(), order, true); this.cachedOrder = order; } public static void setOrder(TopicMap topicmap, TopicIF tt, TopicIF fd, int order, boolean replace) { String value = Ordering.orderToString(order); LocatorIF datatype = DataTypes.TYPE_STRING; TopicIF topicIf = tt; TopicIF typeIf = OntopolyModelUtils.getTopicIF(topicmap, PSI.ON_FIELD_ORDER); TopicIF themeIf = fd; Collection<TopicIF> scope = Collections.singleton(themeIf); Collection<OccurrenceIF> occs = OntopolyModelUtils.findOccurrences(typeIf, topicIf, datatype, scope); if (!occs.isEmpty()) { if (!replace) return; // stop here if we're not replacing Iterator<OccurrenceIF> iter = occs.iterator(); while (iter.hasNext()) { OccurrenceIF occ = iter.next(); occ.remove(); } } OntopolyModelUtils.makeOccurrence(typeIf, topicIf, value, datatype, scope); } /** * Change field order so that this field is ordered directly after the other field. * @param other the field to order after. */ public void moveAfter(FieldAssignment other) { if (ObjectUtils.different(getTopicType(), other.getTopicType())) throw new RuntimeException("Cannot reorder fields that are assigned to different topic types."); List<FieldAssignment> fieldAssignments = getTopicType().getFieldAssignments(); int length = fieldAssignments.size(); // find next field assignment FieldAssignment fa_next = null; int indexOfThis = fieldAssignments.indexOf(this); if (indexOfThis < (length-1)) fa_next = fieldAssignments.get(indexOfThis+1); // get last field order int fieldOrderMax = Ordering.MAX_ORDER; for (int i=0; i < length; i++) { FieldAssignment fa = fieldAssignments.get(i); int fieldOrder = fa.getOrder(); if (fieldOrder != Ordering.MAX_ORDER && (fieldOrderMax == Ordering.MAX_ORDER || fieldOrder > fieldOrderMax)) fieldOrderMax = fieldOrder; } // make sure this field assignment has a field order int fieldOrderThis = getOrder(); if (fieldOrderThis == Ordering.MAX_ORDER) { fieldOrderThis = (fieldOrderMax == Ordering.MAX_ORDER ? 0 : (fieldOrderMax + Ordering.ORDER_INCREMENTS)); setOrder(fieldOrderThis); } // find next available order if (fa_next == null || fa_next.getOrder() == Ordering.MAX_ORDER) { // if no next then just increment other.setOrder(fieldOrderThis + Ordering.ORDER_INCREMENTS); } else { // if next then average this and next field orders int nextAvailableOrder = (fieldOrderThis + fa_next.getOrder())/2; if (nextAvailableOrder != fieldOrderThis) { other.setOrder(nextAvailableOrder); } else { // we need to reshuffle field assignments after this one nextAvailableOrder = fieldOrderThis + Ordering.ORDER_INCREMENTS; other.setOrder(nextAvailableOrder); for (int i=indexOfThis+1; i < length; i++) { FieldAssignment fa = fieldAssignments.get(i); if (!ObjectUtils.equals(fa, other)) { nextAvailableOrder += Ordering.ORDER_INCREMENTS; fa.setOrder(nextAvailableOrder); } } } } } public boolean equals(Object obj) { if (!(obj instanceof FieldAssignment)) return false; FieldAssignment fa = (FieldAssignment) obj; return (topicType.getTopicIF().equals(fa.topicType.getTopicIF()) && fieldDefinition.getTopicIF().equals(fa.getFieldDefinition().getTopicIF())); } public int hashCode() { return topicType.getTopicIF().hashCode() * fieldDefinition.getTopicIF().hashCode(); } }