/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.feature;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.coordsys.CoordinateSystem;
/**
* Metadata for a FeatureCollection: attribute names and types.
* @see FeatureCollection
*/
public class FeatureSchema implements Cloneable, Serializable {
private static final long serialVersionUID = -8627306219650589202L;
//<<TODO:QUESTION>> Is this an efficient implementation? Must cast the Integer to
//an int. [Jon Aquino]
private CoordinateSystem coordinateSystem = CoordinateSystem.UNSPECIFIED;
private HashMap attributeNameToIndexMap = new HashMap();
private int geometryIndex = -1;
private int attributeCount = 0;
private ArrayList attributeNames = new ArrayList();
private ArrayList attributeTypes = new ArrayList();
private ArrayList<Boolean> attributeReadOnly = new ArrayList<Boolean>();
//todo Deep-copy! [Jon Aquino]
//deep copy done 25. Juli 2005 [sstein]
public Object clone() {
try {
FeatureSchema fs = new FeatureSchema();
for (int i = 0; i < this.attributeCount; i++) {
AttributeType at = (AttributeType)this.attributeTypes.get(i);
String aname = (String)this.attributeNames.get(i);
fs.addAttribute(aname,at);
fs.setAttributeReadOnly(i, isAttributeReadOnly(i));
}
fs.setCoordinateSystem(this.coordinateSystem);
return fs;
}
catch (Exception ex) {
Assert.shouldNeverReachHere();
return null;
}
}
/**
* Returns the zero-based index of the attribute with the given name
* (case-sensitive)
*@throws IllegalArgumentException if attributeName is unrecognized
*/
public int getAttributeIndex(String attributeName) {
//<<TODO:RECONSIDER>> Attribute names are currently case sensitive.
//I wonder whether or not this is desirable. [Jon Aquino]
Integer index = (Integer) attributeNameToIndexMap.get(attributeName);
if (index == null) {
throw new IllegalArgumentException(
I18N.get("feature.FeatureSchema.unrecognized-attribute-name")+" " + attributeName);
}
return index.intValue();
}
/**
* Returns whether this FeatureSchema has an attribute with this name
* @param attributeName the name to look up
* @return whether this FeatureSchema has an attribute with this name
*/
public boolean hasAttribute(String attributeName) {
return attributeNameToIndexMap.get(attributeName) != null;
}
/**
* Returns the attribute index of the Geometry, or -1 if there is no
* Geometry attribute
*/
public int getGeometryIndex() {
return geometryIndex;
}
/**
* Returns the (case-sensitive) name of the attribute at the given zero-based index.
*/
public String getAttributeName(int attributeIndex) {
return (String) attributeNames.get(attributeIndex);
}
/**
* Returns whether the attribute at the given zero-based index is a string,
* integer, double, etc.
*/
public AttributeType getAttributeType(int attributeIndex) {
return (AttributeType) attributeTypes.get(attributeIndex);
}
/**
* Returns whether the attribute with the given name (case-sensitive) is a string,
* integer, double, etc.
*/
public AttributeType getAttributeType(String attributeName) {
return getAttributeType(getAttributeIndex(attributeName));
}
/**
* Returns the total number of spatial and non-spatial attributes in this
* FeatureSchema. There are 0 or 1 spatial attributes and 0 or more
* non-spatial attributes.
*/
public int getAttributeCount() {
return attributeCount;
}
/**
* Adds an attribute with the given case-sensitive name.
* @throws AssertionFailedException if a second Geometry is being added
*/
public void addAttribute(String attributeName, AttributeType attributeType) {
if (AttributeType.GEOMETRY == attributeType) {
Assert.isTrue(geometryIndex == -1);
geometryIndex = attributeCount;
}
attributeNames.add(attributeName);
attributeNameToIndexMap.put(attributeName, new Integer(attributeCount));
attributeTypes.add(attributeType);
// default to current implementation - all attributes are editable
// (not readonly)
attributeReadOnly.add(false);
attributeCount++;
}
/**
* Returns whether the two FeatureSchemas have the same attribute names
* with the same types and in the same order.
*/
public boolean equals(Object other) {
return this.equals(other, false);
}
/**
* Returns whether the two FeatureSchemas have the same attribute names
* with the same types and (optionally) in the same order.
*/
public boolean equals(Object other, boolean orderMatters) {
if (!(other instanceof FeatureSchema)) {
return false;
}
FeatureSchema otherFeatureSchema = (FeatureSchema) other;
if (attributeNames.size() != otherFeatureSchema.attributeNames.size()) {
return false;
}
for (int i = 0; i < attributeNames.size(); i++) {
String attributeName = (String) attributeNames.get(i);
if (!otherFeatureSchema.attributeNames.contains(attributeName)) {
return false;
}
if (orderMatters
&& !otherFeatureSchema.attributeNames.get(i).equals(attributeName)) {
return false;
}
if (getAttributeType(attributeName)
!= otherFeatureSchema.getAttributeType(attributeName)) {
return false;
}
}
return true;
}
/**
* Sets the CoordinateSystem associated with this FeatureSchema, but does
* not perform any reprojection.
* @return this FeatureSchema
*/
public FeatureSchema setCoordinateSystem(CoordinateSystem coordinateSystem) {
this.coordinateSystem = coordinateSystem;
return this;
}
/**
* @see #setCoordinateSystem(CoordinateSystem)
*/
public CoordinateSystem getCoordinateSystem() {
return coordinateSystem;
}
/**
* Returns the "readonly" status of the attribute specified by the
* attributeIndex.<br>
* <br>
* A return result of <tt>TRUE</tt> means the a user will not be able to
* edit the attribute in the layer's attribute table, even though the
* layer's "editable" flag has been set to <tt>TRUE</tt>
*
* @param attributeIndex
* The index of the attribute in question.
* @return <tt>TRUE</tt> if the specified attribute has been previously set
* as readonly.
* @see #setAttributeReadOnly(int, boolean)
*/
public boolean isAttributeReadOnly(int attributeIndex) {
return attributeReadOnly.get(attributeIndex);
}
/**
* Sets the "readonly" status of the attribute specified by the
* attributeIndex. <br>
* <br>
* Some schemas (like those that represent database tables) can have
* attributes that should not be modified (like primary keys). Setting such
* an attribute as readonly means a user will not be able to edit the
* attribute in the layer's attribute table, even though the layer's
* "editable" flag has been set to <tt>TRUE</tt>
*
* @param attributeIndex
* The index of the attribute to set
* @param isReadOnly
* A flag that indicates whether the specified attribute should
* be considered "readonly".
* @see #isAttributeReadOnly(int)
*/
public void setAttributeReadOnly(int attributeIndex, boolean isReadOnly) {
attributeReadOnly.set(attributeIndex, isReadOnly);
}
}