package com.revolsys.record.io.format.esri.gdb.xml.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.revolsys.collection.CollectionUtil; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.Lineal; import com.revolsys.geometry.model.Punctual; import com.revolsys.identifier.Identifier; import com.revolsys.io.PathName; import com.revolsys.io.PathUtil; import com.revolsys.record.ArrayRecord; import com.revolsys.record.Record; import com.revolsys.record.io.format.esri.gdb.xml.EsriGeodatabaseXmlConstants; import com.revolsys.record.io.format.esri.gdb.xml.model.enums.FieldType; import com.revolsys.record.io.format.esri.gdb.xml.model.enums.GeometryType; import com.revolsys.record.io.format.esri.gdb.xml.type.EsriGeodatabaseXmlFieldType; import com.revolsys.record.io.format.esri.gdb.xml.type.EsriGeodatabaseXmlFieldTypeRegistry; import com.revolsys.record.property.AreaFieldName; import com.revolsys.record.property.FieldProperties; import com.revolsys.record.property.LengthFieldName; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.record.schema.RecordDefinitionImpl; import com.revolsys.util.Booleans; import com.revolsys.util.Property; public class EsriXmlRecordDefinitionUtil implements EsriGeodatabaseXmlConstants { private static final String DE_TABLE_PROPERTY = EsriXmlRecordDefinitionUtil.class + ".DETable"; public static final EsriGeodatabaseXmlFieldTypeRegistry FIELD_TYPES = EsriGeodatabaseXmlFieldTypeRegistry.INSTANCE; private static Field addField(final DETable table, final FieldDefinition attribute) { final String fieldName = attribute.getName(); final DataType dataType = attribute.getDataType(); final EsriGeodatabaseXmlFieldType fieldType = FIELD_TYPES.getFieldType(dataType); if (fieldType == null) { throw new RuntimeException( "Data type not supported " + dataType + " for " + table.getName() + "." + fieldName); } else { final Field field = new Field(); field.setName(fieldName); field.setType(fieldType.getEsriFieldType()); field.setIsNullable(!attribute.isRequired()); field.setRequired(attribute.isRequired()); int length = fieldType.getFixedLength(); if (length < 0) { length = attribute.getLength(); } field.setLength(length); final int precision; if (fieldType.isUsePrecision()) { precision = attribute.getLength(); } else { precision = 0; } field.setPrecision(precision); final int scale = attribute.getScale(); field.setScale(scale); table.addField(field); return field; } } private static void addField(final RecordDefinitionImpl recordDefinition, final DETable deTable, final String tableName, final Field field, final String fieldName) { final FieldType fieldType = field.getType(); final int precision = field.getPrecision(); final DataType dataType; if (fieldType == FieldType.esriFieldTypeGeometry && deTable instanceof DEFeatureClass) { final DEFeatureClass featureClass = (DEFeatureClass)deTable; final GeometryType shapeType = featureClass.getShapeType(); switch (shapeType) { case esriGeometryPoint: dataType = DataTypes.POINT; break; case esriGeometryMultipoint: dataType = DataTypes.MULTI_POINT; break; case esriGeometryPolyline: dataType = DataTypes.MULTI_LINE_STRING; break; case esriGeometryPolygon: dataType = DataTypes.POLYGON; break; default: throw new RuntimeException( "Unknown geometry type" + shapeType + " for " + tableName + "." + fieldName); } } else if (precision > 0 && (fieldType.equals(FieldType.esriFieldTypeSingle) || fieldType.equals(FieldType.esriFieldTypeDouble))) { dataType = DataTypes.DECIMAL; } else { dataType = EsriGeodatabaseXmlFieldTypeRegistry.INSTANCE.getDataType(fieldType); } final int scale = field.getScale(); int length = field.getLength(); if (precision != 0) { length = precision; } final Boolean required = !field.isIsNullable() || Booleans.getBoolean(field.getRequired()); final FieldDefinition attribute = new FieldDefinition(fieldName, dataType, length, scale, required); recordDefinition.addField(attribute); if (fieldName.equals(tableName + "_ID")) { recordDefinition.setIdFieldName(fieldName); } } private static void addGeometryField(final GeometryType shapeType, final DETable table, final FieldDefinition attribute) { final Field field = addField(table, attribute); final DEFeatureClass featureClass = (DEFeatureClass)table; final SpatialReference spatialReference = featureClass.getSpatialReference(); final GeometryDef geometryDef = new GeometryDef(shapeType, spatialReference); field.setGeometryDef(geometryDef); table.addIndex(field, false, "FDO_GEOMETRY"); } private static void addObjectIdField(final DETable table) { final Field field = new Field(); field.setName(table.getOIDFieldName()); field.setType(FieldType.esriFieldTypeOID); field.setIsNullable(false); field.setLength(4); field.setRequired(true); field.setEditable(false); table.addField(field); table.addIndex(field, true, "FDO_OBJECTID"); } public static DETable getDETable(final RecordDefinition recordDefinition, final SpatialReference spatialReference, final boolean createLengthField, final boolean createAreaField) { DETable table = recordDefinition.getProperty(DE_TABLE_PROPERTY); if (table == null) { table = newDETable(recordDefinition, spatialReference, createLengthField, createAreaField); } return table; } /** * Get a recordDefinition instance for the table definition excluding any ESRI * specific fields. * * @param schemaName * @param deTable * @return */ public static RecordDefinition getRecordDefinition(final String schemaName, final DETable deTable) { return getRecordDefinition(schemaName, deTable, true); } public static RecordDefinition getRecordDefinition(final String schemaName, final DETable deTable, final boolean ignoreEsriFields) { final String tableName = deTable.getName(); final PathName typePath = PathName.newPathName(PathUtil.toPath(schemaName, tableName)); final RecordDefinitionImpl recordDefinition = new RecordDefinitionImpl(typePath); final List<String> ignoreFieldNames = new ArrayList<>(); if (ignoreEsriFields) { ignoreFieldNames.add(deTable.getOIDFieldName()); if (deTable instanceof DEFeatureClass) { final DEFeatureClass featureClass = (DEFeatureClass)deTable; ignoreFieldNames.add(featureClass.getLengthFieldName()); ignoreFieldNames.add(featureClass.getAreaFieldName()); } } for (final Field field : deTable.getFields()) { final String fieldName = field.getName(); if (!ignoreFieldNames.contains(fieldName)) { addField(recordDefinition, deTable, tableName, field, fieldName); } } for (final Index index : deTable.getIndexes()) { final String indexName = index.getName(); if (indexName.endsWith("_PK")) { final List<Field> indexFields = index.getFields(); final Field indexField = CollectionUtil.get(indexFields, 0); final String idName = indexField.getName(); recordDefinition.setIdFieldName(idName); } } if (deTable instanceof DEFeatureClass) { final DEFeatureClass featureClass = (DEFeatureClass)deTable; final String shapeFieldName = featureClass.getShapeFieldName(); recordDefinition.setGeometryFieldName(shapeFieldName); final SpatialReference spatialReference = featureClass.getSpatialReference(); GeometryFactory geometryFactory = spatialReference.getGeometryFactory(); if (featureClass.isHasM()) { geometryFactory = GeometryFactory.fixed(geometryFactory.getCoordinateSystemId(), 4, geometryFactory.getScaleX(), geometryFactory.getScaleY(), geometryFactory.getScaleZ()); } else if (featureClass.isHasZ()) { geometryFactory = GeometryFactory.fixed(geometryFactory.getCoordinateSystemId(), 3, geometryFactory.getScaleX(), geometryFactory.getScaleY(), geometryFactory.getScaleZ()); } final FieldDefinition geometryField = recordDefinition.getGeometryField(); geometryField.setProperty(FieldProperties.GEOMETRY_FACTORY, geometryFactory); } return recordDefinition; } public static RecordDefinition getRecordDefinition(final String schemaName, final Domain domain, final boolean appendIdToName) { final String tableName; if (appendIdToName) { tableName = domain.getName() + "_ID"; } else { tableName = domain.getName(); } final PathName typePath = PathName.newPathName(PathUtil.toPath(schemaName, tableName)); final RecordDefinitionImpl recordDefinition = new RecordDefinitionImpl(typePath); final FieldType fieldType = domain.getFieldType(); final DataType dataType = EsriGeodatabaseXmlFieldTypeRegistry.INSTANCE.getDataType(fieldType); int length = 0; for (final CodedValue codedValue : domain.getCodedValues()) { length = Math.max(length, codedValue.getCode().toString().length()); } recordDefinition.addField(tableName, dataType, length, true); recordDefinition.addField("DESCRIPTION", DataTypes.STRING, 255, true); recordDefinition.setIdFieldIndex(0); return recordDefinition; } public static List<Record> getValues(final RecordDefinition recordDefinition, final Domain domain) { final List<Record> values = new ArrayList<>(); for (final CodedValue codedValue : domain.getCodedValues()) { final Record value = new ArrayRecord(recordDefinition); value.setIdentifier(Identifier.newIdentifier(codedValue.getCode())); value.setValue("DESCRIPTION", codedValue.getName()); values.add(value); } return values; } public static DEFeatureDataset newDEFeatureDataset(final String schemaName, final SpatialReference spatialReference) { final DEFeatureDataset dataset = new DEFeatureDataset(); String name; final int slashIndex = schemaName.lastIndexOf('\\'); if (slashIndex == -1) { name = schemaName; } else { name = schemaName.substring(slashIndex + 1); } dataset.setCatalogPath("\\" + schemaName); dataset.setName(name); final EnvelopeN envelope = new EnvelopeN(spatialReference); dataset.setExtent(envelope); dataset.setSpatialReference(spatialReference); return dataset; } public static List<DEFeatureDataset> newDEFeatureDatasets(final DETable table) { final String parentPath = table.getParentCatalogPath(); if (parentPath.equals("\\")) { return Collections.emptyList(); } else if (table instanceof DEFeatureClass) { final DEFeatureClass featureClass = (DEFeatureClass)table; final String schemaName = parentPath.substring(1); final SpatialReference spatialReference = featureClass.getSpatialReference(); return newDEFeatureDatasets(schemaName, spatialReference); } else { throw new IllegalArgumentException("Expected a " + DEFeatureClass.class.getName()); } } public static List<DEFeatureDataset> newDEFeatureDatasets(final String schemaName, final SpatialReference spatialReference) { final List<DEFeatureDataset> datasets = new ArrayList<>(); String path = ""; for (final String name : schemaName.split("\\\\")) { path += name; final DEFeatureDataset dataset = newDEFeatureDataset(path, spatialReference); datasets.add(dataset); path += "\\"; } return datasets; } public static DETable newDETable(final RecordDefinition recordDefinition, final SpatialReference spatialReference, final boolean createLengthField, final boolean createAreaField) { final String typePath = recordDefinition.getPath(); String schemaPath = PathUtil.getPath(typePath).replaceAll("/", "\\\\"); final FieldDefinition geometryField = recordDefinition.getGeometryField(); boolean hasGeometry = false; DataType geometryDataType = null; GeometryType shapeType = null; if (geometryField != null) { if (spatialReference == null) { throw new IllegalArgumentException( "A Geometry Factory with a coordinate system must be specified."); } geometryDataType = geometryField.getDataType(); if (FIELD_TYPES.getFieldType(geometryDataType) != null) { hasGeometry = true; // TODO Z,m if (geometryDataType.equals(DataTypes.POINT)) { shapeType = GeometryType.esriGeometryPoint; } else if (geometryDataType.equals(DataTypes.MULTI_POINT)) { shapeType = GeometryType.esriGeometryMultipoint; } else if (geometryDataType.equals(DataTypes.LINE_STRING)) { shapeType = GeometryType.esriGeometryPolyline; } else if (geometryDataType.equals(DataTypes.LINEAR_RING)) { shapeType = GeometryType.esriGeometryPolyline; } else if (geometryDataType.equals(DataTypes.MULTI_LINE_STRING)) { shapeType = GeometryType.esriGeometryPolyline; } else if (geometryDataType.equals(DataTypes.POLYGON)) { shapeType = GeometryType.esriGeometryPolygon; } else if (geometryDataType.equals(DataTypes.MULTI_POLYGON)) { shapeType = GeometryType.esriGeometryPolygon; } else { throw new IllegalArgumentException("Unable to detect geometry type"); } } } final List<FieldDefinition> fieldDefinitions = new ArrayList<>(recordDefinition.getFields()); final String path = recordDefinition.getPath(); final String name = PathUtil.getName(path); DETable table; if (hasGeometry) { final DEFeatureClass featureClass = new DEFeatureClass(); table = featureClass; featureClass.setShapeType(shapeType); final String geometryFieldName = geometryField.getName(); featureClass.setShapeFieldName(geometryFieldName); final GeometryFactory geometryFactory = spatialReference.getGeometryFactory(); featureClass.setSpatialReference(spatialReference); featureClass.setHasM(geometryFactory.hasM()); featureClass.setHasZ(geometryFactory.hasZ()); final EnvelopeN envelope = new EnvelopeN(spatialReference); featureClass.setExtent(envelope); final Class<?> geometryClass = geometryDataType.getJavaClass(); if (!Punctual.class.isAssignableFrom(geometryClass)) { final LengthFieldName lengthFieldNameProperty = LengthFieldName .getProperty(recordDefinition); String lengthFieldName = lengthFieldNameProperty.getFieldName(); if (createLengthField) { if (!Property.hasValue(lengthFieldName)) { lengthFieldName = geometryFieldName + "_Length"; lengthFieldNameProperty.setFieldName(lengthFieldName); } if (!recordDefinition.hasField(lengthFieldName)) { fieldDefinitions.add(new FieldDefinition(lengthFieldName, DataTypes.DOUBLE, true)); } } featureClass.setLengthFieldName(lengthFieldName); if (!Lineal.class.isAssignableFrom(geometryClass)) { final AreaFieldName areaFieldNameProperty = AreaFieldName.getProperty(recordDefinition); String areaFieldName = areaFieldNameProperty.getFieldName(); if (createAreaField) { if (!Property.hasValue(areaFieldName)) { areaFieldName = geometryFieldName + "_Area"; areaFieldNameProperty.setFieldName(areaFieldName); } if (!recordDefinition.hasField(areaFieldName)) { fieldDefinitions.add(new FieldDefinition(areaFieldName, DataTypes.DOUBLE, true)); } } featureClass.setAreaFieldName(areaFieldName); } } } else { table = new DETable(); schemaPath = "\\"; } String oidFieldName = recordDefinition .getProperty(EsriGeodatabaseXmlConstants.ESRI_OBJECT_ID_FIELD_NAME); if (!Property.hasValue(oidFieldName)) { oidFieldName = "OBJECTID"; } final String catalogPath; if (schemaPath.equals("\\")) { catalogPath = "\\" + name; } else { catalogPath = schemaPath + "\\" + name; } table.setCatalogPath(catalogPath); table.setName(name); table.setHasOID(true); table.setOIDFieldName(oidFieldName); addObjectIdField(table); final FieldDefinition idField = recordDefinition.getIdField(); for (final FieldDefinition fieldDefinition : fieldDefinitions) { if (fieldDefinition == geometryField) { addGeometryField(shapeType, table, fieldDefinition); } else { final String fieldName = fieldDefinition.getName(); if (!fieldName.equals(oidFieldName)) { final Field field = addField(table, fieldDefinition); if (idField == fieldDefinition) { table.addIndex(field, true, fieldName + "_PK"); } } } } table.setAliasName(name); return table; } }