/* Copyright (c) 2012-2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Victor Olaya (Boundless) - initial implementation */ package org.locationtech.geogig.api; import static com.google.common.base.Preconditions.checkNotNull; import javax.annotation.Nullable; import org.locationtech.geogig.api.RevObject.TYPE; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.vividsolutions.jts.geom.Envelope; /** * An identifier->object id mapping for an object * */ public abstract class Node implements Bounded, Comparable<Node> { /** * The name of the element */ private String name; /** * Optional ID corresponding to metadata for the element */ @Nullable private ObjectId metadataId; /** * Id of the object this ref points to */ private ObjectId objectId; private Node(final String name, final ObjectId oid, final ObjectId metadataId) { checkNotNull(name); checkNotNull(oid); checkNotNull(metadataId); this.name = name; this.objectId = oid; this.metadataId = metadataId.isNull() ? null : metadataId; } public Optional<ObjectId> getMetadataId() { return Optional.fromNullable(metadataId); } /** * @return the name of the {@link RevObject} this node points to */ public String getName() { return name; } /** * @return the id of the {@link RevObject} this Node points to */ public ObjectId getObjectId() { return objectId; } /** * @return the type of {@link RevObject} this node points to */ public abstract TYPE getType(); /** * Provides for natural ordering of {@code Node}, based on {@link #getName() name} */ @Override public int compareTo(Node o) { return name.compareTo(o.getName()); } /** * Hash code is based on name and object id */ @Override public int hashCode() { return 17 ^ getType().hashCode() * name.hashCode() * objectId.hashCode(); } /** * Equality check based on {@link #getName() name}, {@link #getType() type}, and * {@link #getObjectId() objectId}; {@link #getMetadataId()} is NOT part of the equality check. */ @Override public boolean equals(Object o) { if (!(o instanceof Node)) { return false; } Node r = (Node) o; return getType().equals(r.getType()) && name.equals(r.name) && objectId.equals(r.objectId); } /** * @return the Node represented as a readable string. */ @Override public String toString() { return new StringBuilder(getClass().getSimpleName()).append('[').append(getName()) .append(" -> ").append(getObjectId()).append(']').toString(); } @Override public boolean intersects(Envelope env) { return false; } @Override public void expand(Envelope env) { // } public static Node tree(final String name, final ObjectId oid, final ObjectId metadataId) { return create(name, oid, metadataId, TYPE.TREE, null); } public static Node create(final String name, final ObjectId oid, final ObjectId metadataId, final TYPE type, @Nullable final Envelope bounds) { switch (type) { case FEATURE: if (bounds == null || bounds.isNull()) { return new FeatureNode(name, oid, metadataId); } else { return new BoundedFeatureNode(name, oid, metadataId, bounds); } case TREE: if (bounds == null || bounds.isNull()) { return new TreeNode(name, oid, metadataId); } else { return new BoundedTreeNode(name, oid, metadataId, bounds); } default: throw new IllegalArgumentException( "Only FEATURE and TREE nodes can be created, got type " + type); } } private static class TreeNode extends Node { public TreeNode(String name, ObjectId oid, ObjectId mdid) { super(name, oid, mdid); } @Override public final TYPE getType() { return TYPE.TREE; } } private static final class BoundedTreeNode extends TreeNode { // dim0(0),dim0(1),dim1(0),dim1(1) private float[] bounds; public BoundedTreeNode(String name, ObjectId oid, ObjectId mdid, Envelope env) { super(name, oid, mdid); Preconditions.checkArgument(!env.isNull()); if (env.getWidth() == 0 && env.getHeight() == 0) { bounds = new float[2]; } else { bounds = new float[4]; bounds[2] = (float) env.getMaxX(); bounds[3] = (float) env.getMaxY(); } bounds[0] = (float) env.getMinX(); bounds[1] = (float) env.getMinY(); } @Override public boolean intersects(Envelope env) { if (env.isNull()) { return false; } if (bounds.length == 2) { return env.intersects(bounds[0], bounds[1]); } return !(env.getMinX() > bounds[2] || env.getMaxX() < bounds[0] || env.getMinY() > bounds[3] || env.getMaxY() < bounds[1]); } @Override public void expand(Envelope env) { env.expandToInclude(bounds[0], bounds[1]); if (bounds.length > 2) { env.expandToInclude(bounds[2], bounds[3]); } } } private static class FeatureNode extends Node { public FeatureNode(String name, ObjectId oid, ObjectId mdid) { super(name, oid, mdid); } @Override public final TYPE getType() { return TYPE.FEATURE; } } private static final class BoundedFeatureNode extends FeatureNode { // dim0(0),dim1(0),dim0(1),dim1(1) private float[] bounds; public BoundedFeatureNode(String name, ObjectId oid, ObjectId mdid, Envelope env) { super(name, oid, mdid); Preconditions.checkArgument(!env.isNull()); if (env.getWidth() == 0 && env.getHeight() == 0) { bounds = new float[2]; } else { bounds = new float[4]; bounds[2] = (float) env.getMaxX(); bounds[3] = (float) env.getMaxY(); } bounds[0] = (float) env.getMinX(); bounds[1] = (float) env.getMinY(); } @Override public boolean intersects(Envelope env) { if (env.isNull()) { return false; } if (bounds.length == 2) { return env.intersects(bounds[0], bounds[1]); } return !(env.getMinX() > bounds[2] || env.getMaxX() < bounds[0] || env.getMinY() > bounds[3] || env.getMaxY() < bounds[1]); } @Override public void expand(Envelope env) { env.expandToInclude(bounds[0], bounds[1]); if (bounds.length > 2) { env.expandToInclude(bounds[2], bounds[3]); } } } }