* @author Justin Deoliveira, The Open Planning Project * - * - * - * * @source $URL$ + * @since 2.5 + * @version 8.0 */ public class FeatureIdImpl implements FeatureId { - /** underlying fid */ - protected String fid; - protected String origionalFid; - public FeatureIdImpl( String fid ) { - this.fid = fid; - if ( fid == null ) { - throw new NullPointerException( "fid must not be null" ); - } - } - - public String getID() { - return fid; - } - - public void setID( String id ){ - if ( id == null ) { - throw new NullPointerException( "fid must not be null" ); - } - if( origionalFid == null ){ - origionalFid = fid; - } - fid = id; - } - - public boolean matches(Feature feature) { - return feature != null && fid.equals( feature.getIdentifier().getID() ); - } - - public boolean matches(Object object) { - if ( object instanceof Feature ) { - return matches( (Feature) object ); - } - return false; - } - - public String toString() { - return fid; - } - - public boolean equals(Object obj) { - if ( obj instanceof FeatureId) { - return fid.equals( ((FeatureId)obj).getID() ); - } - - return false; - } - - public int hashCode() { - return fid.hashCode(); - } - -} + /** underlying fid */ + protected String fid; + protected String origionalFid; + + public FeatureIdImpl(String fid) { + this.fid = fid; + if (fid == null) { + throw new NullPointerException("fid must not be null"); + } + } + + public String getID() { + return fid; + } + + public void setID(String id) { + if (id == null) { + throw new NullPointerException("fid must not be null"); + } + if (origionalFid == null) { + origionalFid = fid; + } + fid = id; + } + + public boolean matches(Feature feature) { + if( feature == null ){ + return false; + } + return equalsExact( feature.getIdentifier() ); + } + + + public boolean matches(Object object) { + if (object instanceof Feature) { + return matches((Feature) object); + } + return false; + } + + public String toString() { + return fid; + } + + public boolean equals(Object obj) { + if (obj instanceof FeatureId) { + return fid.equals(((FeatureId) obj).getID()); + } + return false; + } + + public int hashCode() { + return fid.hashCode(); + } + + @Override + public boolean equalsExact(FeatureId id) { + if (id instanceof FeatureId) { + return fid.equals( id.getID() ) && + fid.equals( id.getRid() ) && + id.getPreviousRid() == null && + id.getFeatureVersion() == null; + } + return false; + } + + @Override + public boolean equalsFID(FeatureId id) { + if( id == null ) return false; + + return getID().equals(id.getID()); + } + + @Override + public String getRid() { + return getID(); + } + + @Override + public String getPreviousRid() { + return null; + } + + @Override + public String getFeatureVersion() { + return null; + } + +} \ No newline at end of file diff --git a/modules/library/main/src/main/java/org/geotools/filter/identity/FeatureIdVersionedImpl.java b/modules/library/main/src/main/java/org/geotools/filter/identity/FeatureIdVersionedImpl.java new file mode 100644 index 0000000..38337fd --- /dev/null +++ b/modules/library/main/src/main/java/org/geotools/filter/identity/FeatureIdVersionedImpl.java @@ -0,0 +1,102 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2008, 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.filter.identity; + +import org.opengis.feature.Feature; +import org.opengis.filter.identity.FeatureId; +import org.opengis.filter.identity.Version; + +/** + * Implementation of {@link org.opengis.filter.identity.FeatureId} + *
+ * This class is mutable under one condition only; during a commit + * a datastore can update the internal fid to reflect the real identify + * assigned by the database or wfs. + *
+ * @author Justin Deoliveira, The Open Planning Project + * + * @source $URL$ + * @since 2.5 + * @version 8.0 + */ +public class FeatureIdVersionedImpl extends FeatureIdImpl { + protected static String VERSION_SEPARATOR = "@"; + + protected String featureVersion; + protected String previousRid; + + public FeatureIdVersionedImpl(String fid, String version ) { + this( fid, version, null); + } + public FeatureIdVersionedImpl(String fid, String version, String previousRid) { + super( fid ); + this.featureVersion = version; + this.previousRid = previousRid; + } + + + public String toString() { + return fid; + } + + public boolean equals(Object obj) { + if (obj instanceof FeatureId) { + return fid.equals(((FeatureId) obj).getID()); + } + return false; + } + + public int hashCode() { + return fid.hashCode(); + } + + @Override + public boolean equalsExact(FeatureId id) { + if (id instanceof FeatureId) { + return fid.equals( id.getID() ) && + fid.equals( id.getRid() ) && + id.getPreviousRid() == null && + id.getFeatureVersion() == null; + } + return false; + } + + @Override + public boolean equalsFID(FeatureId id) { + if( id == null ) return false; + + return getID().equals(id.getID()); + } + + @Override + public String getRid() { + return featureVersion == null ? getID() : new StringBuilder(getID()) + .append(VERSION_SEPARATOR).append(featureVersion).toString(); + } + + @Override + public String getPreviousRid() { + return previousRid; + } + + @Override + public String getFeatureVersion() { + return featureVersion; + } + + +} \ No newline at end of file diff --git a/modules/library/main/src/main/java/org/geotools/filter/identity/ResourceIdImpl.java b/modules/library/main/src/main/java/org/geotools/filter/identity/ResourceIdImpl.java new file mode 100644 index 0000000..bd09050 --- /dev/null +++ b/modules/library/main/src/main/java/org/geotools/filter/identity/ResourceIdImpl.java @@ -0,0 +1,130 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2008, 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.filter.identity; + +import java.util.Date; + +import org.geotools.util.Utilities; +import org.opengis.filter.identity.ResourceId; +import org.opengis.filter.identity.Version; + +/** + * Implementation of {@link ResourceId} used for Query. + *
+ * This class is mutable under one condition only; during a commit a datastore can update the + * internal fid to reflect the real identify assigned by the database or wfs. + *
+ * + * @author Justin Deoliveira, The Open Planning Project + * @since 8.0 + * @source $URL$ + */ +public class ResourceIdImpl extends FeatureIdVersionedImpl implements ResourceId { + + private Date startTime; + private Date endTime; + + private long version; + + /** + * Obtain a ResourceId based on version lookup. + * + * @param fid feature being queried + * @param featureVersion version used as a reference point + * @param version scope of version based query (first, last, index, all, date, etc...) + */ + public ResourceIdImpl(String fid, String featureVersion, Version version) { + super(fid, featureVersion, null ); + if( version == null ){ + throw new NullPointerException("Version is required for rid lookup"); + } + this.version = version.union(); + } + /** + * + * @param rid + */ + public ResourceIdImpl(String fid, Date start, Date end ) { + super(fid, null, null ); + if( start == null || end == null ){ + throw new NullPointerException("Start and end time are required for a lookup based on a date range"); + } + this.startTime = start; + this.endTime = end; + } + + public void setRid(String rid) { + setID(rid); + } + + public void setPreviousRid(final String previousRid) { + this.previousRid = previousRid; + } + + public void setVersion(final Version version) { + this.version = version.union(); + } + + @Override + public Date getStartTime() { + return startTime; + } + + public void setStartTime(final Date startTime) { + this.startTime = startTime; + } + + @Override + public Date getEndTime() { + return endTime; + } + + public void setEndTime(final Date endTime) { + this.endTime = endTime; + } + + @Override + public Version getVersion() { + return new Version( version ); + } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ResourceId)) { + return false; + } + if (!super.equals(obj)) { + return false; + } + final ResourceId o = (ResourceId) obj; + return Utilities.equals(featureVersion, o.getFeatureVersion()) + && Utilities.equals(previousRid, o.getPreviousRid()) + && Utilities.equals(version, o.getVersion()) + && Utilities.equals(startTime, o.getStartTime()) + && Utilities.equals(endTime, o.getEndTime()); + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = Utilities.hash(previousRid, hash); + hash = Utilities.hash(version, hash); + hash = Utilities.hash(startTime, hash); + hash = Utilities.hash(endTime, hash); + return hash; + } + +} diff --git a/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory.java b/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory.java index b26117f..8f2d7fc 100644 --- a/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory.java +++ b/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory.java @@ -9,6 +9,7 @@ */ package org.opengis.filter; +import java.util.Date; import java.util.List; import java.util.Set; @@ -39,6 +40,8 @@ import org.opengis.filter.expression.Subtract; import org.opengis.filter.identity.FeatureId; import org.opengis.filter.identity.GmlObjectId; import org.opengis.filter.identity.Identifier; +import org.opengis.filter.identity.ResourceId; +import org.opengis.filter.identity.Version; import org.opengis.filter.sort.SortBy; import org.opengis.filter.sort.SortOrder; import org.opengis.filter.spatial.BBOX; @@ -97,10 +100,19 @@ public interface FilterFactory { //////////////////////////////////////////////////////////////////////////////// /** Creates a new feautre id from a string */ FeatureId featureId(String id); + + /** Creates a new feature id with version information */ + FeatureId featureId(String fid, String featureVersion); /** Creates a new gml object id from a string */ GmlObjectId gmlObjectId(String id); + /** ResouceId for identifier based query */ + ResourceId resourceId(String fid, String featureVersion, Version version ); + + /** ResourceId for time based query */ + ResourceId resourceId(String fid, Date startTime, Date endTime); + //////////////////////////////////////////////////////////////////////////////// // // FILTERS diff --git a/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory2.java b/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory2.java index 4f23aa6..5d32ac0 100644 --- a/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory2.java +++ b/modules/library/opengis/src/main/java/org/opengis/filter/FilterFactory2.java @@ -16,6 +16,7 @@ import org.opengis.filter.MultiValuedFilter.MatchAction; import org.opengis.filter.capability.FunctionName; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.PropertyName; +import org.opengis.filter.identity.FeatureId; import org.opengis.filter.spatial.BBOX; import org.opengis.filter.spatial.Beyond; import org.opengis.filter.spatial.Contains; @@ -121,6 +122,8 @@ public interface FilterFactory2 extends FilterFactory { // //////////////////////////////////////////////////////////////////////////////// + Id id( FeatureId ...fids); + /** * Retrieves the value of a {@linkplain org.opengis.feature.Feature feature}'s property. * @param name Name of attribute referenced diff --git a/modules/library/opengis/src/main/java/org/opengis/filter/identity/FeatureId.java b/modules/library/opengis/src/main/java/org/opengis/filter/identity/FeatureId.java index 741c9cb..77b2e55 100644 --- a/modules/library/opengis/src/main/java/org/opengis/filter/identity/FeatureId.java +++ b/modules/library/opengis/src/main/java/org/opengis/filter/identity/FeatureId.java @@ -14,10 +14,9 @@ import org.opengis.annotation.XmlElement; /** * Feature identifier. + *
* Features are identified as strings. * - * - * * @source $URL$ * @version Implementation specification 1.0 * @author Chris Dillard (SYS Technologies) @@ -26,12 +25,18 @@ import org.opengis.annotation.XmlElement; */ @XmlElement("FeatureId") public interface FeatureId extends Identifier { + + public static final char VERSION_SEPARATOR = '@'; + /** * The identifier value, which is a string. */ @XmlElement("fid") String getID(); + // + // Query and Test methods used to test a feature or record + // /** * Evaluates the identifer value against the given feature. * @@ -39,4 +44,68 @@ public interface FeatureId extends Identifier { * @return {@code true} if a match, otherwise {@code false}. */ boolean matches(Object feature); + + /** Check if the provided FeatureId is an exact match (including any optional version + * information). + * + * @param id + * @return true if this is an exact match (including any optional version information) + */ + boolean equalsExact(FeatureId id); + + /** + * Checks if the provided FeatureId reflects the same feature. + *
+ * This comparison does not compare any optional version information. + * + * @param id + * @return true if both identifiers describe the same feature (does not compare version information). + */ + boolean equalsFID(FeatureId id); + + // + // Filter 2.0 Versioning Support + // + // The following methods are optional and are used as part of the FeatureId data + // structure to report any available version information associated with a resoruce. + // + /** + * id of the resource that shall be selected by the predicate. + *
+ * Equals to {@link #getID()} if no feature version is provided, or + * {@code getID() + "@" + getFeatureVersion()} if {@code getFeatureVersion() != null} + * + *
+ * If an implementation that references this International Standard supports versioning, the rid + * shall be a system generated hash containing a logical resource identifier and a version + * number. The specific details of the hash are implementation dependant and shall be opaque to + * a client + *
+ *+ * If versioning is not supported, the same value than {@link FeatureId#getID()} shall be + * returned. + *
+ * @return Resource identifier made up of FID (combined with FeatureVersion if available) + */ + @XmlElement("rid") + String getRid(); + + /** + * previousRid attribute may be used, in implementations that support versioning, to report the + * previous identifier of a resource. + * + * @return Previous rid if available; or {@code null} + */ + @XmlElement("previousRid") + String getPreviousRid(); + + /** + * Version identifier for the feature instance, may be {@code null} + * + * @see #getID() + * @see #getRid() + * @return Optional version information; {@code null} if not available + */ + String getFeatureVersion(); + } diff --git a/modules/library/opengis/src/main/java/org/opengis/filter/identity/ResourceId.java b/modules/library/opengis/src/main/java/org/opengis/filter/identity/ResourceId.java new file mode 100644 index 0000000..ba26f91 --- /dev/null +++ b/modules/library/opengis/src/main/java/org/opengis/filter/identity/ResourceId.java @@ -0,0 +1,59 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2011, Open Source Geospatial Foundation (OSGeo) + * (C) 2005, Open Geospatial Consortium Inc. + * + * All Rights Reserved. http://www.opengis.org/legal/ + */ +package org.opengis.filter.identity; + +import java.util.Date; + +import org.opengis.annotation.XmlElement; + +/** + * Resource identifier as per FES 2.0. + *+ * Please note this is a query object for use with the Filter Id filter as shown: + *
Filter filter = filterFactory.id(
+ * ff.featureId("CITY.123"),
+ * ff.resourceId("CITY.123",Version.Action.PREVIOUS) );
+ * In cases where a plain FetureId is used for lookup it is understood to refer to
+ * Version.Action.LAST.
+ * + * If an implementation that references this International Standard does not support versioning, any + * value specified for the attributes {@link #getPreviousRid() previousRid}, {@link #getVersion() + * version}, {@link #getStartTime() startTime}, and {@link #getEndTime() endTime} shall be ignored + * and the predicate shall always select the single version that is available. + *
+ */ +@XmlElement("ResourceId") +public interface ResourceId extends FeatureId { + + /** + * Used to navigate versions of a resource. + *+ * @return Version based resource query; or {@code null} if using a start and end time + */ + @XmlElement("version") + Version getVersion(); + + /** + * Used to select versions of a resource between start and end time. + *
+ * @return start time for a time based query; or {@code null} if using version + */ + @XmlElement("startTime") + Date getStartTime(); + + /** + * Used to select versions of a resource between start and end time. + * + * @return end time for a time based query; or {@code null} if using version + */ + @XmlElement("endTime") + Date getEndTime(); + +} \ No newline at end of file diff --git a/modules/library/opengis/src/main/java/org/opengis/filter/identity/Version.java b/modules/library/opengis/src/main/java/org/opengis/filter/identity/Version.java new file mode 100644 index 0000000..f388575 --- /dev/null +++ b/modules/library/opengis/src/main/java/org/opengis/filter/identity/Version.java @@ -0,0 +1,175 @@ +package org.opengis.filter.identity; + +import java.util.Date; + +/** + * Union type class for the {@code Version} Union type in FES 2.0. + *+ * The union is actually captured as a union inside a single long field. + * + * @invariant {@code #getVersionAction() != null || #getIndex() != null || #getDateTime() != null} + */ +public final class Version { + + /** + * The VersionAction attribute may also be the strings FIRST, LATEST, PREVIOUS, NEXT and ALL. + */ + public enum Action { + /** Select the first version of a resource. */ + FIRST, + /** Select the most recent version of a resource. */ + LAST, + /** Select the previous version of a resource relative to the version specified using the rid attribute. */ + NEXT, + /** Select the next version of a resource relative to the version specified using the rid attribute. */ + PREVIOUS, + /** Select all available version of a resource. */ + ALL; + + public static Action lookup(int ordinal) { + for (Action action : Action.values()) { + if (action.ordinal() == ordinal) { + return action; + } + } + return null; // not found + } + } + + static final long UNION_MASK = 0x0FFFFFFFFFFFFFFFl; + + static final long UNION_INTEGER = 0x1000000000000000l; + + static final long UNION_DATE = 0x2000000000000000l; + + static final long UNION_ACTION = 0x4000000000000000l; + + /** + * The union is represented as + *
intdate.getTime()action.ordinal()
+ * To restore please use new Version( union )
+ *
+ * @return memento holding the contents of a Version object
+ */
+ public long union(){
+ return union;
+ }
+
+ /**
+ * Version requested using a predefined constant.
+ *
+ * The versionAction attribute may also be the strings FIRST, LATEST, PREVIOUS, NEXT and ALL. The token FIRST shall select the first version of a + * resource. The token LATEST shall select the most recent version of a resource. The PREVIOUS and NEXT tokens shall select the previous or next + * version of a resource relative to the version specified using the rid attribute. The token ALL shall select all available version of a + * resource. + * + * @return Version requested using a predefined constant. + */ + public Action getVersionAction() { + if ((UNION_ACTION & union) > 0) { + int ordinal = (int) (union & UNION_MASK); + Action action = Action.lookup(ordinal); + return action; + } + return null; + } + + /** + * Version index requested. + * + * @return true if the Version is supplied by an index + */ + public boolean isIndex() { + return (UNION_INTEGER & union) > 0; + } + + /** + * Version requested as defined by an index (from 1 throught to the latest). + *
+ * The version attribute may be an integer N indicating that the Nth version of the resource shall be selected. The first version of a resource + * shall be numbered 1. If N exceeds the number of versions available, the latest version of the resource shall be selected. + * + * @return index of version requested (from 1 through to the latest) + */ + public Integer getIndex() { + if ((UNION_INTEGER & union) > 0) { + int index = (int) (union & UNION_MASK); + return index; + } + return null; + } + + public boolean isDateTime() { + return (UNION_DATE & union) > 0; + } + + /** + * Version requested as the closest to the provided date. + *
+ * The version attribute may also be date indicating that the version of the resource closest to the specified date shall be selected.
+ *
+ * @return date of version requested
+ */
+ public Date getDateTime() {
+ if ((UNION_DATE & union) > 0) {
+ long time = (union & UNION_MASK);
+ return new Date(time);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Version)) {
+ return false;
+ }
+ Version v = (Version) o;
+ return union == v.union;
+ }
+
+ @Override
+ public int hashCode() {
+ return 17 * (int) union;
+ }
+}
\ No newline at end of file
diff --git a/modules/library/opengis/src/test/java/org/opengis/filter/identity/VersionTest.java b/modules/library/opengis/src/test/java/org/opengis/filter/identity/VersionTest.java
new file mode 100644
index 0000000..eb3b9f5
--- /dev/null
+++ b/modules/library/opengis/src/test/java/org/opengis/filter/identity/VersionTest.java
@@ -0,0 +1,59 @@
+package org.opengis.filter.identity;
+
+import static org.junit.Assert.*;
+
+import java.util.Date;
+
+import org.junit.Test;
+import org.opengis.filter.identity.Version.Action;
+
+public class VersionTest {
+
+ @Test
+ public void bitwise() {
+ for (Action action : Action.values()) {
+ long encoded = Version.UNION_ACTION | ((long) action.ordinal());
+
+ assertTrue( (encoded & Version.UNION_ACTION) > 0 );
+ long decoded = Version.UNION_MASK & ((long)encoded);
+
+ Action found = Action.lookup((int)decoded);
+ assertEquals( action, found );
+ }
+
+ }
+
+ @Test
+ public void versionInteger() {
+ Version version = new Version(1234567890);
+
+ assertNotNull(version.getIndex());
+ assertTrue( version.isIndex() );
+ assertEquals(1234567890, (int) version.getIndex());
+
+ assertFalse( version.isVersionAction() );
+ assertNull(version.getVersionAction());
+
+ assertNull(version.getDateTime());
+ }
+
+ @Test
+ public void versionDate() {
+ Date now = new Date();
+
+ Version version = new Version(now);
+
+ assertEquals(now, version.getDateTime());
+ assertNull(version.getIndex());
+ assertNull(version.getVersionAction());
+ }
+
+ @Test
+ public void versionAction() {
+ Version version = new Version(Version.Action.ALL);
+
+ assertEquals(Version.Action.ALL, version.getVersionAction());
+ assertNull(version.getIndex());
+ assertNull(version.getDateTime());
+ }
+}
diff --git a/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java b/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java
index ac08923..a5ba0ce 100644
--- a/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java
+++ b/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java
@@ -883,17 +883,7 @@ abstract class ArcSdeFeatureWriter implements FeatureWriter