/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2011, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.geotools.factory.CommonFactoryFinder; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.expression.PropertyName; /** * Represents the joining of two feature types within a {@link Query}. * <p> * The Join class is similar to Query in that it allows one to specify a FeatureType name, a set of * properties, and a filter. A Join must specify: * <ol> * <li>A type name that references the feature type to join to, see {@link #getTypeName()} * <li>A join filter that describes how to join, see {@link #getJoinFilter()} * </ol> * Optionally a Join may also specify: * <ul> * <li>A set of property names constraining the attributes of joined features, see {@link #getProperties()} * <li>A secondary filter used to constrained features from the joined feature type, see {@link #getFilter()} * <li>An alias for the joined feature type, which can be used in the join filter to disambiguate * attributes of the feature types being joined, see {@link #getAlias()} * <li>A join type specifying what type of join (inner, outer, etc...) should be performed, see {@link #getType()} * </ul> * </p> * * @author Justin Deoliveira, OpenGeo * * @since 8.0 */ public class Join { /** * filter factory */ static final FilterFactory ff = CommonFactoryFinder.getFilterFactory(null); /** * type of join */ public static enum Type { INNER, OUTER; } /** join type */ Type type; /** * the feature type name being joined to */ String typeName; /** * attributes to fetch for this feature type */ List<PropertyName> properties = Query.ALL_PROPERTIES; /** * the join predicate */ Filter join; /** * additional predicate against the target of the join */ Filter filter; /** * The alias to be used for the typeName in this join */ String alias; /** * Constructs a join. * * @param typeName The name of the feature type to join to. * @param join The filter specifying the join condition between the two feature types being * joined. */ public Join(String typeName, Filter join) { this.typeName = typeName; this.join = join; this.type = Type.INNER; this.properties = Query.ALL_PROPERTIES; this.filter = Filter.INCLUDE; this.alias = null; } /** * Constructs a join from another. */ public Join(Join other) { this.typeName = other.getTypeName(); this.join = other.getJoinFilter(); this.filter = other.getFilter(); this.type = other.getType(); this.properties = other.getProperties(); this.filter = other.getFilter(); this.alias = other.getAlias(); } /** * The name of the feature type being joined to. * <p> * This name may be the same as the name of the primary feature type, this is how a self join is * specified. * </p> */ public String getTypeName() { return typeName; } /** * The filter defining the join condition between the primary feature type and the feature * type being joined to. * <p> * This filter should be a comparison operator whose contents are two {@link PropertyName} * instances. For example: * <pre> * new Join("theOtherType", propertyIsEqualTo(propertyName("foo"), propertyName("bar"))); * </pre> * In instances where the two property names involved in the join are the same a prefix or * alias must be used to differentiate: * <pre> * Join j = new Join("theOtherType", propertyIsEqualTo(propertyName("foo"), propertyName("other.bar"))); * j.alias("other"); * </pre> * </p> */ public Filter getJoinFilter() { return join; } /** * Sets the join type. * @see #getType() */ public void setType(Type type) { this.type = type; } /** * The type of the join. * <p> * {@link Type#INNER} is the default join type. * </p> * */ public Type getType() { return type; } /** * List of properties specifying which attributes of joined features to obtain. * <p> * This method has the same purpose as {@link Query#getProperties()}. * </p> */ public List<PropertyName> getProperties() { if (properties == Query.ALL_PROPERTIES) { return properties; } return Collections.unmodifiableList(properties); } /** * Sets list of properties specifying which attributes of joined features to obtain. * <p> * This method has the same purpose as {@link Query#setProperties(List)}. * </p> */ public void setProperties(List<PropertyName> properties) { this.properties = properties; } /** * List of property names specifying which attributes of joined features to obtain. * <p> * This method has the same purpose as {@link Query#getPropertyNames()}. * </p> */ public String[] getPropertyNames() { if (properties == Query.ALL_PROPERTIES) { return Query.ALL_NAMES; } String[] names = new String[properties.size()]; for (int i = 0; i < names.length; i++) { names[i] = properties.get(i).getPropertyName(); } return names; } /** * Sets the filter used to constrain which features from the joined feature type to return. * * @see #getFilter() */ public void setFilter(Filter filter) { this.filter = filter; } /** * Filter used to constrain which features from the joined feature type to return. * <p> * This filter must only reference attributes from the joined feature type, and not of any other * feature types involved in the join. * </p> */ public Filter getFilter() { return filter; } /** * Sets an alias for the feature type being joined to. * @see #getAlias() */ public void setAlias(String alias) { this.alias = alias; } /** * An alias for the feature type being joined to. * <p> * This method is useful in cases where the two feature types being joined contain attributes * identically named, or in cases where a self join is being performed: * <pre> * Join j = new Join("theOtherType", PropertyIsEqualTo(PropertyName("foo"), PropertyName("other.foo"))); * j.setAlias("other"); * </pre> * </p> * * @see #getJoinFilter() */ public String getAlias() { return alias; } /** * Convenience method that returns the attribute name to be used for this join. * <p> * Convenience for: * <code> * <pre> * return getAlias() != null ? getAlias() : getTypeName(); * </pre> * </code> * </p> */ public String attributeName() { return getAlias() != null ? getAlias() : getTypeName(); } /** * Chaining method for {@link #getProperties()} */ public Join properties(String... properties) { this.properties = new ArrayList(); for (String p : properties) { this.properties.add(ff.property(p)); } return this; } /** * Chaining method for {@link #setFilter(Filter)} */ public Join filter(Filter filter) { setFilter(filter); return this; } /** * Chaining method for {@link #setAlias(String)} */ public Join alias(String alias) { setAlias(alias); return this; } /** * Chaining method for {@link #setType(Type)} */ public Join type(Type type) { setType(type); return this; } }