/* * #! * 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.query.impl.basic; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.infoset.impl.basic.URILocator; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.DataTypes; import net.ontopia.topicmaps.core.TopicNameIF; import net.ontopia.topicmaps.core.OccurrenceIF; import net.ontopia.topicmaps.core.ReifiableIF; import net.ontopia.topicmaps.core.ScopedIF; import net.ontopia.topicmaps.core.TMObjectIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.TopicMapIF; import net.ontopia.topicmaps.core.TypedIF; import net.ontopia.topicmaps.core.VariantNameIF; import net.ontopia.topicmaps.core.index.IndexIF; import net.ontopia.topicmaps.core.index.NameIndexIF; import net.ontopia.topicmaps.core.index.OccurrenceIndexIF; import net.ontopia.topicmaps.core.index.ScopeIndexIF; import net.ontopia.topicmaps.query.core.InvalidQueryException; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.utils.ObjectUtils; /** * INTERNAL: Shared utilities for the predicate implementations. */ public class PredicateUtils { // generic public static final int NO_OPERATION = 0; // object-to-one public static final int NAME_TO_TOPIC = 1; public static final int OCCURRENCE_TO_TOPIC = 2; public static final int ROLE_TO_ASSOCIATION = 3; public static final int REIFIER_TO_REIFIED = 4; public static final int REIFIED_TO_REIFIER = 5; public static final int INSTANCE_TO_TYPE = 6; public static final int OBJECT_TO_VALUE = 7; public static final int ROLE_TO_PLAYER = 8; public static final int OBJECT_TO_RESOURCE = 9; public static final int VNAME_TO_TNAME = 10; public static final int SUBJLOC_TO_TOPIC = 12; public static final int SRCLOC_TO_OBJECT = 13; public static final int SUBJID_TO_TOPIC = 14; public static final int STR_TO_LENGTH = 15; public static final int OBJECT_TO_ID = 16; public static final int ID_TO_OBJECT = 17; public static final int EQUAL_TO_EQUAL = 18; public static final int OBJECT_TO_DATATYPE = 19; // object-to-many public static final int ASSOCIATION_TO_ROLE = 101; public static final int SCOPED_TO_THEME = 102; public static final int RESOURCE_TO_OBJECT = 103; public static final int TNAME_TO_VNAME = 104; public static final int THEME_TO_SCOPED = 105; public static final int TOPIC_TO_SUBJLOC = 11; public static final int TOPIC_TO_SUBJID = 106; public static final int OBJECT_TO_SRCLOC = 107; public static final int TOPIC_TO_OCCURRENCE = 108; public static final int TOPIC_TO_NAME = 109; // filter public static final int FILTER_ROLE_PLAYER = 201; public static final int FILTER_REIFIES = 202; public static final int FILTER_TOPIC_NAME = 203; public static final int FILTER_VALUE = 204; public static final int FILTER_TYPE = 205; public static final int FILTER_VARIANT = 206; public static final int FILTER_SCOPE = 207; public static final int FILTER_ASSOCIATION_ROLE = 208; public static final int FILTER_RESOURCE = 209; public static final int FILTER_SUBJLOC = 210; public static final int FILTER_SUBJID = 211; public static final int FILTER_SRCLOC = 212; public static final int FILTER_STR_STARTS_WITH = 213; public static final int FILTER_STR_CONTAINS = 214; public static final int FILTER_STR_LENGTH = 215; public static final int FILTER_ID = 216; public static final int FILTER_EQUALS = 217; public static final int FILTER_OCCURRENCE = 218; public static final int FILTER_STR_ENDS_WITH = 219; public static final int FILTER_DATATYPE = 220; // collection-to-one public static final int GENERATE_REIFIES = 301; public static final int GENERATE_VALUE = 302; public static final int GENERATE_TYPE = 303; public static final int GENERATE_RESOURCES = 304; public static final int GENERATE_SUBJLOC = 305; public static final int GENERATE_ID = 306; public static final int GENERATE_DATATYPE = 307; // collection-to-many public static final int GENERATE_TOPIC_NAME = 401; public static final int GENERATE_OCCURRENCE = 402; public static final int GENERATE_VARIANTS = 403; public static final int GENERATE_ROLES = 404; public static final int GENERATE_ROLE_PLAYER = 405; public static final int GENERATE_SCOPED = 406; public static final int GENERATE_SUBJID = 407; public static final int GENERATE_SRCLOC = 408; public static QueryMatches objectToOne(QueryMatches matches, int fromix, int toix, Class fromclass, int operation) throws InvalidQueryException { QueryMatches result = new QueryMatches(matches); for (int ix = 0; ix <= matches.last; ix++) { if (!fromclass.isInstance(matches.data[ix][fromix])) continue; Object[] newRow = (Object[]) matches.data[ix].clone(); switch(operation) { case ROLE_TO_PLAYER: newRow[toix] = ((AssociationRoleIF) newRow[fromix]).getPlayer(); if (newRow[toix] == null) continue; break; case NAME_TO_TOPIC: newRow[toix] = ((TopicNameIF) newRow[fromix]).getTopic(); break; case OCCURRENCE_TO_TOPIC: newRow[toix] = ((OccurrenceIF) newRow[fromix]).getTopic(); break; case ROLE_TO_ASSOCIATION: newRow[toix] = ((AssociationRoleIF) newRow[fromix]).getAssociation(); break; case REIFIER_TO_REIFIED: newRow[toix] = ((TopicIF) newRow[fromix]).getReified(); if (newRow[toix] == null) continue; break; case REIFIED_TO_REIFIER: newRow[toix] = ((ReifiableIF) newRow[fromix]).getReifier(); if (newRow[toix] == null) continue; break; case INSTANCE_TO_TYPE: newRow[toix] = ((TypedIF) newRow[fromix]).getType(); if (newRow[toix] == null) continue; break; case OBJECT_TO_VALUE: if (newRow[fromix] instanceof TopicNameIF) newRow[toix] = ((TopicNameIF) newRow[fromix]).getValue(); else if (newRow[fromix] instanceof VariantNameIF) { VariantNameIF vn = (VariantNameIF) newRow[fromix]; if (ObjectUtils.equals(vn.getDataType(), DataTypes.TYPE_URI)) // exclude xsd:anyURI continue; newRow[toix] = vn.getValue(); } else if (newRow[fromix] instanceof OccurrenceIF) { OccurrenceIF occ = (OccurrenceIF) newRow[fromix]; if (ObjectUtils.equals(occ.getDataType(), DataTypes.TYPE_URI)) // exclude xsd:anyURI continue; newRow[toix] = occ.getValue(); } if (newRow[toix] == null) continue; break; case OBJECT_TO_RESOURCE: if (newRow[fromix] instanceof VariantNameIF) newRow[toix] = ((VariantNameIF) newRow[fromix]).getLocator(); else if (newRow[fromix] instanceof OccurrenceIF) newRow[toix] = ((OccurrenceIF) newRow[fromix]).getLocator(); if (newRow[toix] == null) continue; else newRow[toix] = ((LocatorIF) newRow[toix]).getAddress(); break; case VNAME_TO_TNAME: newRow[toix] = ((VariantNameIF) newRow[fromix]).getTopicName(); break; case SUBJLOC_TO_TOPIC: LocatorIF loc = getLocator(newRow[fromix]); newRow[toix] = matches.getQueryContext().getTopicMap().getTopicBySubjectLocator(loc); if (newRow[toix] == null) continue; break; case SUBJID_TO_TOPIC: loc = getLocator(newRow[fromix]); newRow[toix] = matches.getQueryContext().getTopicMap().getTopicBySubjectIdentifier(loc); if (newRow[toix] == null) continue; break; case SRCLOC_TO_OBJECT: loc = getLocator(newRow[fromix]); newRow[toix] = matches.getQueryContext().getTopicMap().getObjectByItemIdentifier(loc); if (newRow[toix] == null) continue; break; case STR_TO_LENGTH: String str = (String) newRow[fromix]; newRow[toix] = new Integer(str.length()); break; case OBJECT_TO_ID: TMObjectIF obj = (TMObjectIF) newRow[fromix]; newRow[toix] = obj.getObjectId(); break; case ID_TO_OBJECT: newRow[toix] = matches.getQueryContext().getTopicMap().getObjectById((String) newRow[fromix]); break; case EQUAL_TO_EQUAL: newRow[toix] = newRow[fromix]; break; case OBJECT_TO_DATATYPE: if (newRow[fromix] instanceof VariantNameIF) newRow[toix] = ((VariantNameIF) newRow[fromix]).getDataType(); else if (newRow[fromix] instanceof OccurrenceIF) newRow[toix] = ((OccurrenceIF) newRow[fromix]).getDataType(); if (newRow[toix] == null) continue; else newRow[toix] = ((LocatorIF) newRow[toix]).getAddress(); break; default: throw new OntopiaRuntimeException("INTERNAL ERROR: Unknown operation code " + operation); } if (result.last+1 == result.size) result.increaseCapacity(); result.last++; result.data[result.last] = newRow; } return result; } public static QueryMatches objectToMany(QueryMatches matches, int fromix, int toix, Class fromclass, int operation, IndexIF index) throws InvalidQueryException { return objectToMany(matches, fromix, toix, fromclass, operation, index, null); } public static QueryMatches objectToMany(QueryMatches matches, int fromix, int toix, Class fromclass, int operation, IndexIF index1, IndexIF index2) throws InvalidQueryException { QueryMatches result = new QueryMatches(matches); for (int ix = 0; ix <= matches.last; ix++) { if (!fromclass.isInstance(matches.data[ix][fromix])) continue; Collection objects = null; switch(operation) { case ASSOCIATION_TO_ROLE: objects = ((AssociationIF) matches.data[ix][fromix]).getRoles(); break; case SCOPED_TO_THEME: objects = ((ScopedIF) matches.data[ix][fromix]).getScope(); break; case RESOURCE_TO_OBJECT: LocatorIF locator = getLocator(matches.data[ix][fromix]); objects = new ArrayList(((NameIndexIF) index1).getVariants(locator.getAddress(), DataTypes.TYPE_URI)); objects.addAll(((OccurrenceIndexIF) index2).getOccurrences(locator.getAddress(), DataTypes.TYPE_URI)); break; case TNAME_TO_VNAME: objects = ((TopicNameIF) matches.data[ix][fromix]).getVariants(); break; case TOPIC_TO_SUBJLOC: objects = new ArrayList(); Iterator it = ((TopicIF) matches.data[ix][fromix]).getSubjectLocators().iterator(); while (it.hasNext()) objects.add(((LocatorIF) it.next()).getAddress()); break; case TOPIC_TO_SUBJID: objects = new ArrayList(); it = ((TopicIF) matches.data[ix][fromix]).getSubjectIdentifiers().iterator(); while (it.hasNext()) objects.add(((LocatorIF) it.next()).getAddress()); break; case OBJECT_TO_SRCLOC: objects = new ArrayList(); it = ((TMObjectIF) matches.data[ix][fromix]).getItemIdentifiers().iterator(); while (it.hasNext()) objects.add(((LocatorIF) it.next()).getAddress()); break; case THEME_TO_SCOPED: TopicIF theme = (TopicIF) matches.data[ix][fromix]; ScopeIndexIF sindex = (ScopeIndexIF) index1; objects = new ArrayList(sindex.getAssociations(theme)); objects.addAll(sindex.getTopicNames(theme)); objects.addAll(sindex.getVariants(theme)); objects.addAll(sindex.getOccurrences(theme)); break; case TOPIC_TO_OCCURRENCE: TopicIF topic = (TopicIF) matches.data[ix][fromix]; objects = topic.getOccurrences(); break; case TOPIC_TO_NAME: topic = (TopicIF) matches.data[ix][fromix]; objects = topic.getTopicNames(); } if (objects.isEmpty()) continue; while (result.last + objects.size() >= result.size) result.increaseCapacity(); Object[] values = objects.toArray(); for (int valueix = 0; valueix < values.length; valueix++) { Object[] newRow = (Object[]) matches.data[ix].clone(); newRow[toix] = values[valueix]; result.data[++result.last] = newRow; } } return result; } public static QueryMatches filter(QueryMatches matches, int ix1, int ix2, Class class1, Class class2, int operation) throws InvalidQueryException { QueryMatches result = new QueryMatches(matches); for (int ix = 0; ix <= matches.last; ix++) { // verify types of objects if (!class1.isInstance(matches.data[ix][ix1]) || !class2.isInstance(matches.data[ix][ix2])) continue; // find correct value Object object = null; switch (operation) { case FILTER_TYPE: object = ((TypedIF) matches.data[ix][ix1]).getType(); break; case FILTER_SCOPE: Object theme = matches.data[ix][ix2]; if (((ScopedIF) matches.data[ix][ix1]).getScope().contains(theme)) object = theme; break; case FILTER_ROLE_PLAYER: AssociationRoleIF role = (AssociationRoleIF) matches.data[ix][ix1]; object = role.getPlayer(); break; case FILTER_REIFIES: ReifiableIF reified = (ReifiableIF) matches.data[ix][ix1]; object = reified.getReifier(); break; case FILTER_TOPIC_NAME: TopicNameIF bn = (TopicNameIF) matches.data[ix][ix1]; object = bn.getTopic(); break; case FILTER_OCCURRENCE: OccurrenceIF occ = (OccurrenceIF) matches.data[ix][ix1]; object = occ.getTopic(); break; case FILTER_VALUE: if (matches.data[ix][ix1] instanceof TopicNameIF) object = ((TopicNameIF) matches.data[ix][ix1]).getValue(); else if (matches.data[ix][ix1] instanceof VariantNameIF) { VariantNameIF vn = (VariantNameIF) matches.data[ix][ix1]; if (ObjectUtils.different(vn.getDataType(), DataTypes.TYPE_URI)) // exclude xsd:anyURI object = vn.getValue(); } else if (matches.data[ix][ix1] instanceof OccurrenceIF) { OccurrenceIF occ2 = (OccurrenceIF) matches.data[ix][ix1]; if (ObjectUtils.different(occ2.getDataType(), DataTypes.TYPE_URI)) // exclude xsd:anyURI object = occ2.getValue(); } break; case FILTER_VARIANT: object = ((VariantNameIF) matches.data[ix][ix1]).getTopicName(); break; case FILTER_ASSOCIATION_ROLE: object = ((AssociationRoleIF) matches.data[ix][ix1]).getAssociation(); break; case FILTER_RESOURCE: if (matches.data[ix][ix1] instanceof OccurrenceIF) object = ((OccurrenceIF) matches.data[ix][ix1]).getLocator(); else if (matches.data[ix][ix1] instanceof VariantNameIF) object = ((VariantNameIF) matches.data[ix][ix1]).getLocator(); if (object != null) object = ((LocatorIF) object).getAddress(); break; case FILTER_SUBJLOC: LocatorIF loc = getLocator(matches.data[ix][ix2]); if (((TopicIF) matches.data[ix][ix1]).getSubjectLocators().contains(loc)) object = matches.data[ix][ix2]; break; case FILTER_SUBJID: loc = getLocator(matches.data[ix][ix2]); if (((TopicIF) matches.data[ix][ix1]).getSubjectIdentifiers().contains(loc)) object = matches.data[ix][ix2]; break; case FILTER_SRCLOC: loc = getLocator(matches.data[ix][ix2]); if (((TMObjectIF) matches.data[ix][ix1]).getItemIdentifiers().contains(loc)) object = matches.data[ix][ix2]; break; case FILTER_ID: object = ((TMObjectIF) matches.data[ix][ix1]).getObjectId(); break; case FILTER_STR_STARTS_WITH: String str1 = (String) matches.data[ix][ix1]; String str2 = (String) matches.data[ix][ix2]; if (str1.startsWith(str2)) object = str2; break; case FILTER_STR_ENDS_WITH: String string1 = (String) matches.data[ix][ix1]; String string2 = (String) matches.data[ix][ix2]; if (string1.endsWith(string2)) object = string2; break; case FILTER_STR_CONTAINS: str1 = (String) matches.data[ix][ix1]; str2 = (String) matches.data[ix][ix2]; if (str1.indexOf(str2) != -1) object = str2; break; case FILTER_STR_LENGTH: str1 = (String) matches.data[ix][ix1]; Number num = (Number) matches.data[ix][ix2]; if (str1.length() == num.intValue()) object = num; break; case FILTER_EQUALS: object = matches.data[ix][ix1]; break; case FILTER_DATATYPE: LocatorIF dt; if (matches.data[ix][ix1] instanceof VariantNameIF) dt = ((VariantNameIF) matches.data[ix][ix1]).getDataType(); else if (matches.data[ix][ix1] instanceof OccurrenceIF) dt = ((OccurrenceIF) matches.data[ix][ix1]).getDataType(); else throw new InvalidQueryException("Internal error!"); object = dt.getAddress(); break; case NO_OPERATION: break; } // check value found against value given if (object == null || !object.equals(matches.data[ix][ix2])) continue; // ok, add match if (result.last+1 == result.size) result.increaseCapacity(); result.last++; result.data[result.last] = matches.data[ix]; } return result; } /** * Given a collection of objects, produce a single new object from * each of them and put it into the toix column. So for each row in * the input result, either remove it (if it's of the wrong type, or * if it has no new object) or insert the new object in the toix * column. */ public static QueryMatches collectionToOne(QueryMatches matches, Object[] objects, int fromix, int toix, int operation) { QueryMatches result = new QueryMatches(matches); // for each object in input collection... for (int oix = 0; oix < objects.length; oix++) { // generate other object Object other = null; switch(operation) { case GENERATE_REIFIES: other = ((TopicIF) objects[oix]).getReified(); break; case GENERATE_VALUE: if (objects[oix] instanceof TopicNameIF) other = ((TopicNameIF) objects[oix]).getValue(); else if (objects[oix] instanceof VariantNameIF) { VariantNameIF vn = (VariantNameIF) objects[oix]; if (ObjectUtils.different(vn.getDataType(), DataTypes.TYPE_URI)) // exclude xsd:anyURI other = vn.getValue(); } else { OccurrenceIF occ = (OccurrenceIF) objects[oix]; if (ObjectUtils.different(occ.getDataType(), DataTypes.TYPE_URI)) // exclude xsd:anyURI other = occ.getValue(); } break; case GENERATE_TYPE: other = ((TypedIF) objects[oix]).getType(); break; case GENERATE_RESOURCES: if (objects[oix] instanceof OccurrenceIF) other = ((OccurrenceIF) objects[oix]).getLocator(); else other = ((VariantNameIF) objects[oix]).getLocator(); if (other != null) other = ((LocatorIF) other).getAddress(); break; case GENERATE_ID: other = ((TMObjectIF) objects[oix]).getObjectId(); break; case GENERATE_DATATYPE: if (objects[oix] instanceof VariantNameIF) other = ((VariantNameIF) objects[oix]).getDataType().getAddress(); else if (objects[oix] instanceof OccurrenceIF) other = ((OccurrenceIF) objects[oix]).getDataType().getAddress(); break; case NO_OPERATION: other = objects[oix]; } // does this one generate matches? if (other == null) continue; // duplicate match set for (int ix = 0; ix <= matches.last; ix++) { Object[] newRow = (Object[]) matches.data[ix].clone(); newRow[fromix] = objects[oix]; newRow[toix] = other; if (result.last+1 == result.size) result.increaseCapacity(); result.last++; result.data[result.last] = newRow; } } return result; } /** * Assuming both columns are empty, iterate over a collection of * start values, binding the first column to the start value, and * the second column to each object in a collection generated from * the start value. */ public static QueryMatches generateFromCollection(QueryMatches matches, int fromix, int toix, Collection startset, int operation) { QueryMatches result = new QueryMatches(matches); // for each object in input collection for (Iterator it = startset.iterator(); it.hasNext(); ) { Object next = it.next(); // generate new collection Collection contained = null; switch(operation) { case GENERATE_TOPIC_NAME: contained = ((TopicIF) next).getTopicNames(); break; case GENERATE_OCCURRENCE: contained = ((TopicIF) next).getOccurrences(); break; case GENERATE_VARIANTS: contained = ((TopicNameIF) next).getVariants(); break; case GENERATE_ROLES: contained = ((AssociationIF) next).getRoles(); break; case GENERATE_ROLE_PLAYER: contained = ((TopicIF) next).getRoles(); break; case GENERATE_SCOPED: contained = ((ScopedIF) next).getScope(); break; case GENERATE_SUBJLOC: contained = new ArrayList(); Iterator it2 = ((TopicIF) next).getSubjectLocators().iterator(); while (it2.hasNext()) contained.add(((LocatorIF) it2.next()).getAddress()); break; case GENERATE_SUBJID: contained = new ArrayList(); it2 = ((TopicIF) next).getSubjectIdentifiers().iterator(); while (it2.hasNext()) contained.add(((LocatorIF) it2.next()).getAddress()); break; case GENERATE_SRCLOC: contained = new ArrayList(); it2 = ((TMObjectIF) next).getItemIdentifiers().iterator(); while (it2.hasNext()) contained.add(((LocatorIF) it2.next()).getAddress()); break; } // does this one generate matches? if (contained.isEmpty()) continue; // for each contained object... Iterator it2 = contained.iterator(); while (it2.hasNext()) { Object object = it2.next(); // ...duplicate match set for (int ix = 0; ix <= matches.last; ix++) { Object[] newRow = (Object[]) matches.data[ix].clone(); newRow[fromix] = next; newRow[toix] = object; if (result.last+1 == result.size) result.increaseCapacity(); result.last++; result.data[result.last] = newRow; } } } return result; } /** * Filters a result set so that only rows where the object in a * specific column is an instance of a specific class are accepted. */ public static QueryMatches filterClass(QueryMatches matches, int ix1, Class klass) { QueryMatches result = new QueryMatches(matches); for (int ix = 0; ix <= matches.last; ix++) { // verify type of object if (!klass.isInstance(matches.data[ix][ix1])) continue; if (result.last+1 == result.size) result.increaseCapacity(); result.last++; result.data[result.last] = matches.data[ix]; } return result; } /** * Returns all topic map objects in the topic map. */ public static Collection getAllObjects(TopicMapIF topicmap) { Collection objects = new ArrayList(topicmap.getTopics().size() * 10); objects.add(topicmap); objects.addAll(topicmap.getTopics()); objects.addAll(topicmap.getAssociations()); Iterator it = topicmap.getAssociations().iterator(); while (it.hasNext()) { AssociationIF assoc = (AssociationIF) it.next(); objects.addAll(assoc.getRoles()); } it = topicmap.getTopics().iterator(); while (it.hasNext()) { TopicIF topic = (TopicIF) it.next(); objects.addAll(topic.getTopicNames()); objects.addAll(topic.getOccurrences()); Iterator it2 = topic.getTopicNames().iterator(); while (it2.hasNext()) { TopicNameIF bn = (TopicNameIF) it2.next(); objects.addAll(bn.getVariants()); } } return objects; } // Comparison public static int compare(Object o1, Object o2) { if (!(o1 instanceof Comparable)) { int h1 = (o1 == null ? 0 : o1.hashCode()); int h2 = (o2 == null ? 0 : o2.hashCode()); return h1 - h2; } try { return ((Comparable) o1).compareTo(o2); } catch (ClassCastException e) { // if we compare a String with an Integer we wind up here return o1.getClass().getName().compareTo(o2.getClass().getName()); } } // Internal private static LocatorIF getLocator(Object locator) throws InvalidQueryException { try { return new URILocator((String) locator); } catch (java.net.MalformedURLException e) { throw new InvalidQueryException("Illegal URI: " + locator); } } }