/*
* 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.arcsde.data;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.geotools.arcsde.ArcSdeException;
import org.geotools.arcsde.session.Command;
import org.geotools.arcsde.session.ISession;
import org.geotools.arcsde.session.SdeRow;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import com.esri.sde.sdk.client.SeColumnDefinition;
import com.esri.sde.sdk.client.SeConnection;
import com.esri.sde.sdk.client.SeException;
import com.esri.sde.sdk.client.SeLayer;
import com.esri.sde.sdk.client.SeRegistration;
import com.esri.sde.sdk.client.SeShape;
import com.esri.sde.sdk.client.SeTable;
/**
* Strategy object used to manage the different ways an ArcSDE server handles row identity.
* <p>
* The supported strategies are:
* <ul>
* <li>SDE managed mode: a column is assigned by the sde engine to be the feature id (it uses to be
* called OBJECTID)
* <li>User managed: a user specified row is used as the fid column.
* <li>Shape fid: if none of the above, the fid happens to be the identifier of the geometry column
* </ul>
* </p>
*
* @author Gabriel Roldan, Axios Engineering
* @version $Id$
* @source $URL:
* http://svn.geotools.org/geotools/trunk/gt/modules/plugin/arcsde/datastore/src/main/java
* /org/geotools/arcsde/data/FIDReader.java $
*/
public abstract class FIDReader {
protected String layerName;
/** column name holding the feature id attribute */
private String fidColumn;
private int columnIndex;
/**
* Creates a new FIDStrategy object.
*
* @param fidColumns
*
*/
private FIDReader(String layerName, String fidColumn) {
this.layerName = layerName;
this.fidColumn = fidColumn;
}
public String getFidColumn() {
return fidColumn;
}
public void setColumnIndex(int fidIndex) {
this.columnIndex = fidIndex;
}
public int getColumnIndex() {
return this.columnIndex;
}
public long readFid(SdeRow row) throws IOException {
Object fid = row.getObject(this.columnIndex);
return ((Number) fid).longValue();
}
/**
* Returns the attribute names of the FeatureType passed to the constructor.
*
* @param the
* feature type containing the properties the client code is interested in. May well
* be a subset of the full set of attributes in the SeLayer
* @return the list of property names to actually fetch for a given feature type, taking into
* account the ones that possibly need to be fetched to generate the feature id, even if
* they're not part of the schema.
* @throws IOException
* if an arcsde exception is thrown somehow.
*/
public String[] getPropertiesToFetch(SimpleFeatureType schema) throws IOException {
List<String> attNames = new ArrayList<String>(schema.getAttributeCount() + 1);
// /List attDescriptors = Descriptors.nodes(schema.getDescriptor());
List<AttributeDescriptor> attDescriptors = schema.getAttributeDescriptors();
for (AttributeDescriptor property : attDescriptors) {
attNames.add(property.getLocalName());
}
String fidColumn = getFidColumn();
int fidIndex = attNames.indexOf(fidColumn);
if (fidColumn != null && fidIndex == -1) {
attNames.add(fidColumn);
fidIndex = attNames.size() - 1;
}
setColumnIndex(fidIndex);
return attNames.toArray(new String[attNames.size()]);
}
/**
* Returns a FID strategy appropriate for the given SeLayer
*
* @param session
* @param tableName
* @return
* @throws IOException
*/
public static FIDReader getFidReader(final ISession session, final SeTable table,
final SeLayer layer, final SeRegistration reg) throws IOException {
return session.issue(new Command<FIDReader>() {
@Override
public FIDReader execute(final ISession session, final SeConnection connection)
throws SeException, IOException {
return getFidReaderInternal(session, table, layer, reg);
}
});
}
/**
* Only to be called from inside a command
*
* @see #getFidReader(ISession, SeTable, SeLayer, SeRegistration)
*/
private static FIDReader getFidReaderInternal(ISession session, SeTable table, SeLayer layer,
SeRegistration reg) throws IOException, ArcSdeException {
FIDReader fidReader = null;
final String tableName = reg.getTableName();
try {
// final int rowIdAllocationType = reg.getRowIdAllocation();
final int rowIdColumnType = reg.getRowIdColumnType();
final String rowIdColumnName = reg.getRowIdColumnName();
int rowIdColumnIndex = -1;
SeColumnDefinition[] schema = session.describe(table);
for (int index = 0; index < schema.length; index++) {
if (schema[index].getName().equals(rowIdColumnName)) {
rowIdColumnIndex = index;
break;
}
}
if (rowIdColumnType == SeRegistration.SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE) {
// use column name, value maintained by sde
fidReader = new SdeManagedFidReader(tableName, rowIdColumnName);
} else if (rowIdColumnType == SeRegistration.SE_REGISTRATION_ROW_ID_COLUMN_TYPE_USER) {
// use column name, value maintained by user
fidReader = new UserManagedFidReader(tableName, rowIdColumnName);
} else if (rowIdColumnType == SeRegistration.SE_REGISTRATION_ROW_ID_COLUMN_TYPE_NONE) {
// use geometry id
String shapeColName = layer.getSpatialColumn();
String shapeIdColName = layer.getShapeAttributeName(SeLayer.SE_SHAPE_ATTRIBUTE_FID);
fidReader = new ShapeFidReader(tableName, shapeColName, shapeIdColName);
} else {
// may have been returned 0, meaning there is no registered
// column id
throw new IllegalStateException("Unkown ArcSDE row ID registration type: "
+ rowIdColumnType + " for layer " + tableName);
}
fidReader.setColumnIndex(rowIdColumnIndex);
return fidReader;
} catch (SeException e) {
throw new ArcSdeException("Obtaining FID strategy for " + tableName, e);
}
}
public static class ShapeFidReader extends FIDReader {
/**
* Name of the Shape, populated as a side effect of getPropertiesToFetch()
*/
private final String shapeColName;
/**
* Index of the Shape, populated as a side effect of getPropertiesToFetch()
*/
private int shapeIndex;
public ShapeFidReader(final String layerName, final String shapeColName,
final String shapeIdColName) {
super(layerName, shapeIdColName);
this.shapeColName = shapeColName;
this.shapeIndex = -1;
}
@Override
public long readFid(SdeRow row) throws IOException {
long longFid;
if (shapeIndex != -1) {
// we have the shape, so SHAPE.fid couldn't be retrieved
// at the same time, need to get the shape and ask it for the id
try {
SeShape shape = row.getShape(shapeIndex);
if (shape == null) {
throw new NullPointerException("Can't get FID from " + layerName
+ " as it has SHAPE fid reading strategy and got a null shape");
}
longFid = shape.getFeatureId().longValue();
} catch (SeException e) {
throw new ArcSdeException("Getting fid from shape", e);
}
} else {
int shapeIdIndex = getColumnIndex();
Integer id = (Integer) row.getObject(shapeIdIndex);
longFid = id.longValue();
}
return longFid;
}
/**
* Overrides to include the geometry column whether it is required by the {@code schema} or
* not, since we need to get the fid from the geometry id.
*/
@Override
public String[] getPropertiesToFetch(SimpleFeatureType schema) throws IOException {
List<String> attNames = new ArrayList<String>(schema.getAttributeCount() + 1);
// /List attDescriptors = Descriptors.nodes(schema.getDescriptor());
List<AttributeDescriptor> attDescriptors = schema.getAttributeDescriptors();
for (AttributeDescriptor property : attDescriptors) {
attNames.add(property.getLocalName());
}
shapeIndex = attNames.indexOf(shapeColName);
if (shapeIndex == -1) {
String fidColumn = getFidColumn();
int fidIndex = attNames.indexOf(shapeColName);
if (fidIndex == -1) {
attNames.add(fidColumn);
fidIndex = attNames.size() - 1;
}
setColumnIndex(fidIndex);
}
return attNames.toArray(new String[attNames.size()]);
}
}
public static class SdeManagedFidReader extends FIDReader {
public SdeManagedFidReader(final String layerName, final String rowIdColName) {
super(layerName, rowIdColName);
}
}
public static class UserManagedFidReader extends FIDReader {
public UserManagedFidReader(final String layerName, final String rowIdColName) {
super(layerName, rowIdColName);
}
}
public static final FIDReader NULL_READER = new FIDReader(null, null) {
@Override
public long readFid(SdeRow row) throws IOException {
return (long) (10000 * Math.random());
}
};
}