// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.core.domain.v0_6;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
import org.openstreetmap.osmosis.core.store.StoreClassRegister;
import org.openstreetmap.osmosis.core.store.StoreReader;
import org.openstreetmap.osmosis.core.store.StoreWriter;
/**
* A data class representing a single OSM relation.
*
* @author Brett Henderson
*/
public class Relation extends Entity implements Comparable<Relation> {
private List<RelationMember> members;
/**
* Creates a new instance.
*
* @param id
* The unique identifier.
* @param version
* The version of the entity.
* @param timestamp
* The last updated timestamp.
* @param user
* The user that last modified this entity.
* @param changesetId
* The id of the changeset that this version of the entity was created by.
* @deprecated As of 0.40, replaced by Relation(entityData).
*/
public Relation(long id, int version, Date timestamp, OsmUser user, long changesetId) {
// Chain to the more-specific constructor
this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId);
}
/**
* Creates a new instance.
*
* @param id
* The unique identifier.
* @param version
* The version of the entity.
* @param timestampContainer
* The container holding the timestamp in an alternative
* timestamp representation.
* @param user
* The user that last modified this entity.
* @param changesetId
* The id of the changeset that this version of the entity was created by.
* @deprecated As of 0.40, replaced by Relation(entityData).
*/
public Relation(long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId) {
super(id, version, timestampContainer, user, changesetId);
this.members = new ArrayList<RelationMember>();
}
/**
* Creates a new instance.
*
* @param entityData
* The common entity data.
*/
public Relation(CommonEntityData entityData) {
super(entityData);
this.members = new ArrayList<RelationMember>();
}
/**
* Creates a new instance.
*
* @param id
* The unique identifier.
* @param version
* The version of the entity.
* @param timestamp
* The last updated timestamp.
* @param user
* The user that last modified this entity.
* @param changesetId
* The id of the changeset that this version of the entity was created by.
* @param tags
* The tags to apply to the object.
* @param members
* The members to apply to the object.
* @deprecated As of 0.40, replaced by Relation(entityData, members).
*/
public Relation(
long id, int version, Date timestamp, OsmUser user, long changesetId, Collection<Tag> tags,
List<RelationMember> members) {
// Chain to the more-specific constructor
this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId, tags, members);
}
/**
* Creates a new instance.
*
* @param id
* The unique identifier.
* @param version
* The version of the entity.
* @param timestampContainer
* The container holding the timestamp in an alternative
* timestamp representation.
* @param user
* The user that last modified this entity.
* @param changesetId
* The id of the changeset that this version of the entity was created by.
* @param tags
* The tags to apply to the object.
* @param members
* The members to apply to the object.
* @deprecated As of 0.40, replaced by Relation(entityData, members).
*/
public Relation(
long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId,
Collection<Tag> tags, List<RelationMember> members) {
super(id, version, timestampContainer, user, changesetId, tags);
this.members = new ArrayList<RelationMember>(members);
}
/**
* Creates a new instance.
*
* @param entityData
* The common entity data.
* @param members
* The members to apply to the object.
*/
public Relation(
CommonEntityData entityData, List<RelationMember> members) {
super(entityData);
this.members = new ArrayList<RelationMember>(members);
}
/**
* Creates a new instance.
*
* @param originalRelation
* The relation to clone from.
*/
private Relation(Relation originalRelation) {
super(originalRelation);
this.members = new ArrayList<RelationMember>(originalRelation.members);
}
/**
* Creates a new instance.
*
* @param sr
* The store to read state from.
* @param scr
* Maintains the mapping between classes and their identifiers
* within the store.
*/
public Relation(StoreReader sr, StoreClassRegister scr) {
super(sr, scr);
int featureCount;
featureCount = sr.readInteger();
members = new ArrayList<RelationMember>();
for (int i = 0; i < featureCount; i++) {
members.add(new RelationMember(sr, scr));
}
}
/**
* {@inheritDoc}
*/
@Override
public void store(StoreWriter sw, StoreClassRegister scr) {
super.store(sw, scr);
sw.writeInteger(members.size());
for (RelationMember relationMember : members) {
relationMember.store(sw, scr);
}
}
/**
* {@inheritDoc}
*/
@Override
public EntityType getType() {
return EntityType.Relation;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (o instanceof Relation) {
return compareTo((Relation) o) == 0;
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
/*
* As per the hashCode definition, this doesn't have to be unique it
* just has to return the same value for any two objects that compare
* equal. Using both id and version will provide a good distribution of
* values but is simple to calculate.
*/
return (int) getId() + getVersion();
}
/**
* Compares this member list to the specified member list. The bigger list
* is considered bigger, if that is equal then each relation member is
* compared.
*
* @param comparisonMemberList
* The member list to compare to.
* @return 0 if equal, < 0 if considered "smaller", and > 0 if
* considered "bigger".
*/
protected int compareMemberList(Collection<RelationMember> comparisonMemberList) {
Iterator<RelationMember> i;
Iterator<RelationMember> j;
// The list with the most entities is considered bigger.
if (members.size() != comparisonMemberList.size()) {
return members.size() - comparisonMemberList.size();
}
// Check the individual node references.
i = members.iterator();
j = comparisonMemberList.iterator();
while (i.hasNext()) {
int result = i.next().compareTo(j.next());
if (result != 0) {
return result;
}
}
// There are no differences.
return 0;
}
/**
* Compares this relation to the specified relation. The relation comparison
* is based on a comparison of id, version, timestamp, and tags in that
* order.
*
* @param comparisonRelation
* The relation to compare to.
* @return 0 if equal, < 0 if considered "smaller", and > 0 if
* considered "bigger".
*/
public int compareTo(Relation comparisonRelation) {
int memberListResult;
if (this.getId() < comparisonRelation.getId()) {
return -1;
}
if (this.getId() > comparisonRelation.getId()) {
return 1;
}
if (this.getVersion() < comparisonRelation.getVersion()) {
return -1;
}
if (this.getVersion() > comparisonRelation.getVersion()) {
return 1;
}
if (this.getTimestamp() == null && comparisonRelation.getTimestamp() != null) {
return -1;
}
if (this.getTimestamp() != null && comparisonRelation.getTimestamp() == null) {
return 1;
}
if (this.getTimestamp() != null && comparisonRelation.getTimestamp() != null) {
int result;
result = this.getTimestamp().compareTo(comparisonRelation.getTimestamp());
if (result != 0) {
return result;
}
}
memberListResult = compareMemberList(
comparisonRelation.members
);
if (memberListResult != 0) {
return memberListResult;
}
return compareTags(comparisonRelation.getTags());
}
/**
* {@inheritDoc}
*/
@Override
public void makeReadOnly() {
if (!isReadOnly()) {
members = Collections.unmodifiableList(members);
}
super.makeReadOnly();
}
/**
* {@inheritDoc}
*/
@Override
public Relation getWriteableInstance() {
if (isReadOnly()) {
return new Relation(this);
} else {
return this;
}
}
/**
* Returns the attached list of relation members. The returned list is
* read-only.
*
* @return The member list.
*/
public List<RelationMember> getMembers() {
return members;
}
/**
* ${@inheritDoc}.
*/
@Override
public String toString() {
String type = null;
Collection<Tag> tags = getTags();
for (Tag tag : tags) {
if (tag.getKey() != null && tag.getKey().equalsIgnoreCase("type")) {
type = tag.getValue();
break;
}
}
if (type != null) {
return "Relation(id=" + getId() + ", #tags=" + getTags().size() + ", type='" + type + "')";
}
return "Relation(id=" + getId() + ", #tags=" + getTags().size() + ")";
}
}