| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.hugegraph.backend.id; |
| |
| import org.apache.hugegraph.exception.NotFoundException; |
| import org.apache.hugegraph.perf.PerfUtil.Watched; |
| import org.apache.hugegraph.structure.HugeVertex; |
| import org.apache.hugegraph.type.HugeType; |
| import org.apache.hugegraph.type.define.Directions; |
| import org.apache.hugegraph.type.define.HugeKeys; |
| import org.apache.hugegraph.util.E; |
| import org.apache.hugegraph.util.StringEncoding; |
| |
| /** |
| * Class used to format and parse id of edge, the edge id consists of: |
| * { source-vertex-id + edge-label + edge-name + target-vertex-id } |
| * NOTE: if we use `entry.type()` which is IN or OUT as a part of id, |
| * an edge's id will be different due to different directions (belongs |
| * to 2 owner vertex) |
| */ |
| public class EdgeId implements Id { |
| |
| public static final HugeKeys[] KEYS = new HugeKeys[]{ |
| HugeKeys.OWNER_VERTEX, |
| HugeKeys.DIRECTION, |
| HugeKeys.LABEL, |
| HugeKeys.SORT_VALUES, |
| HugeKeys.OTHER_VERTEX |
| }; |
| |
| private final Id ownerVertexId; |
| private final Directions direction; |
| private final Id edgeLabelId; |
| private final String sortValues; |
| private final Id otherVertexId; |
| |
| private final boolean directed; |
| private String cache; |
| |
| public EdgeId(HugeVertex ownerVertex, Directions direction, |
| Id edgeLabelId, String sortValues, HugeVertex otherVertex) { |
| this(ownerVertex.id(), direction, edgeLabelId, |
| sortValues, otherVertex.id()); |
| } |
| |
| public EdgeId(Id ownerVertexId, Directions direction, Id edgeLabelId, |
| String sortValues, Id otherVertexId) { |
| this(ownerVertexId, direction, edgeLabelId, |
| sortValues, otherVertexId, false); |
| } |
| |
| public EdgeId(Id ownerVertexId, Directions direction, Id edgeLabelId, |
| String sortValues, Id otherVertexId, boolean directed) { |
| this.ownerVertexId = ownerVertexId; |
| this.direction = direction; |
| this.edgeLabelId = edgeLabelId; |
| this.sortValues = sortValues; |
| this.otherVertexId = otherVertexId; |
| this.directed = directed; |
| this.cache = null; |
| } |
| |
| @Watched |
| public EdgeId switchDirection() { |
| Directions direction = this.direction.opposite(); |
| return new EdgeId(this.otherVertexId, direction, this.edgeLabelId, |
| this.sortValues, this.ownerVertexId, this.directed); |
| } |
| |
| public EdgeId directed(boolean directed) { |
| return new EdgeId(this.ownerVertexId, this.direction, this.edgeLabelId, |
| this.sortValues, this.otherVertexId, directed); |
| } |
| |
| private Id sourceVertexId() { |
| return this.direction == Directions.OUT ? |
| this.ownerVertexId : |
| this.otherVertexId; |
| } |
| |
| private Id targetVertexId() { |
| return this.direction == Directions.OUT ? |
| this.otherVertexId : |
| this.ownerVertexId; |
| } |
| |
| public Id ownerVertexId() { |
| return this.ownerVertexId; |
| } |
| |
| public Id edgeLabelId() { |
| return this.edgeLabelId; |
| } |
| |
| public Directions direction() { |
| return this.direction; |
| } |
| |
| public byte directionCode() { |
| return directionToCode(this.direction); |
| } |
| |
| public String sortValues() { |
| return this.sortValues; |
| } |
| |
| public Id otherVertexId() { |
| return this.otherVertexId; |
| } |
| |
| @Override |
| public Object asObject() { |
| return this.asString(); |
| } |
| |
| @Override |
| public String asString() { |
| if (this.cache != null) { |
| return this.cache; |
| } |
| if (this.directed) { |
| this.cache = SplicingIdGenerator.concat( |
| IdUtil.writeString(this.ownerVertexId), |
| this.direction.type().string(), |
| IdUtil.writeLong(this.edgeLabelId), |
| this.sortValues, |
| IdUtil.writeString(this.otherVertexId)); |
| } else { |
| this.cache = SplicingIdGenerator.concat( |
| IdUtil.writeString(this.sourceVertexId()), |
| IdUtil.writeLong(this.edgeLabelId), |
| this.sortValues, |
| IdUtil.writeString(this.targetVertexId())); |
| } |
| return this.cache; |
| } |
| |
| @Override |
| public long asLong() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public byte[] asBytes() { |
| return StringEncoding.encode(this.asString()); |
| } |
| |
| @Override |
| public int length() { |
| return this.asString().length(); |
| } |
| |
| @Override |
| public IdType type() { |
| return IdType.EDGE; |
| } |
| |
| @Override |
| public int compareTo(Id other) { |
| return this.asString().compareTo(other.asString()); |
| } |
| |
| @Override |
| public int hashCode() { |
| if (this.directed) { |
| return this.ownerVertexId.hashCode() ^ |
| this.direction.hashCode() ^ |
| this.edgeLabelId.hashCode() ^ |
| this.sortValues.hashCode() ^ |
| this.otherVertexId.hashCode(); |
| } else { |
| return this.sourceVertexId().hashCode() ^ |
| this.edgeLabelId.hashCode() ^ |
| this.sortValues.hashCode() ^ |
| this.targetVertexId().hashCode(); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (!(object instanceof EdgeId)) { |
| return false; |
| } |
| EdgeId other = (EdgeId) object; |
| if (this.directed) { |
| return this.ownerVertexId.equals(other.ownerVertexId) && |
| this.direction == other.direction && |
| this.edgeLabelId.equals(other.edgeLabelId) && |
| this.sortValues.equals(other.sortValues) && |
| this.otherVertexId.equals(other.otherVertexId); |
| } else { |
| return this.sourceVertexId().equals(other.sourceVertexId()) && |
| this.edgeLabelId.equals(other.edgeLabelId) && |
| this.sortValues.equals(other.sortValues) && |
| this.targetVertexId().equals(other.targetVertexId()); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return this.asString(); |
| } |
| |
| public static byte directionToCode(Directions direction) { |
| return direction.type().code(); |
| } |
| |
| public static Directions directionFromCode(byte code) { |
| return Directions.convert(HugeType.fromCode(code)); |
| } |
| |
| public static boolean isOutDirectionFromCode(byte code) { |
| return code == HugeType.EDGE_OUT.code(); |
| } |
| |
| public static EdgeId parse(String id) throws NotFoundException { |
| return parse(id, false); |
| } |
| |
| public static EdgeId parse(String id, boolean returnNullIfError) |
| throws NotFoundException { |
| String[] idParts = SplicingIdGenerator.split(id); |
| if (!(idParts.length == 4 || idParts.length == 5)) { |
| if (returnNullIfError) { |
| return null; |
| } |
| throw new NotFoundException("Edge id must be formatted as 4~5 " + |
| "parts, but got %s parts: '%s'", |
| idParts.length, id); |
| } |
| try { |
| if (idParts.length == 4) { |
| Id ownerVertexId = IdUtil.readString(idParts[0]); |
| Id edgeLabelId = IdUtil.readLong(idParts[1]); |
| String sortValues = idParts[2]; |
| Id otherVertexId = IdUtil.readString(idParts[3]); |
| return new EdgeId(ownerVertexId, Directions.OUT, edgeLabelId, |
| sortValues, otherVertexId); |
| } else { |
| assert idParts.length == 5; |
| Id ownerVertexId = IdUtil.readString(idParts[0]); |
| HugeType direction = HugeType.fromString(idParts[1]); |
| Id edgeLabelId = IdUtil.readLong(idParts[2]); |
| String sortValues = idParts[3]; |
| Id otherVertexId = IdUtil.readString(idParts[4]); |
| return new EdgeId(ownerVertexId, Directions.convert(direction), |
| edgeLabelId, sortValues, otherVertexId); |
| } |
| } catch (Throwable e) { |
| if (returnNullIfError) { |
| return null; |
| } |
| throw new NotFoundException("Invalid format of edge id '%s'", |
| e, id); |
| } |
| } |
| |
| public static Id parseStoredString(String id) { |
| String[] idParts = split(id); |
| E.checkArgument(idParts.length == 4, "Invalid id format: %s", id); |
| Id ownerVertexId = IdUtil.readStoredString(idParts[0]); |
| Id edgeLabelId = IdGenerator.ofStoredString(idParts[1], IdType.LONG); |
| String sortValues = idParts[2]; |
| Id otherVertexId = IdUtil.readStoredString(idParts[3]); |
| return new EdgeId(ownerVertexId, Directions.OUT, edgeLabelId, |
| sortValues, otherVertexId); |
| } |
| |
| public static String asStoredString(Id id) { |
| EdgeId eid = (EdgeId) id; |
| return SplicingIdGenerator.concat( |
| IdUtil.writeStoredString(eid.sourceVertexId()), |
| IdGenerator.asStoredString(eid.edgeLabelId()), |
| eid.sortValues(), |
| IdUtil.writeStoredString(eid.targetVertexId())); |
| } |
| |
| public static String concat(String... ids) { |
| return SplicingIdGenerator.concat(ids); |
| } |
| |
| public static String[] split(Id id) { |
| return EdgeId.split(id.asString()); |
| } |
| |
| public static String[] split(String id) { |
| return SplicingIdGenerator.split(id); |
| } |
| } |