diff --git a/.circleci/config.yml b/.circleci/config.yml index c92d5c8091..3bfa765f4f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,7 +67,7 @@ jobs: - install-bazel-linux-rbe - checkout - run-bazel-rbe: - command: bazel test //concept/test/... --test_output=errors + command: bazel test //test/behaviour/concept:concept-it --test_output=errors test-keyspace: machine: true diff --git a/BUILD b/BUILD index 95f3943554..c10102e252 100644 --- a/BUILD +++ b/BUILD @@ -28,6 +28,7 @@ exports_files(["VERSION", "RELEASE_TEMPLATE.md", "deployment.properties"]) java_library( name = "client-java", srcs = glob([ + "answer/*.java", "concept/*.java", "exception/*.java", "rpc/*.java", @@ -38,8 +39,6 @@ java_library( # External dependencies from @graknlabs "@graknlabs_graql//java:graql", "@graknlabs_protocol//grpc/java:protocol", - "@graknlabs_grakn_core//api:api", - "@graknlabs_grakn_core//concept:concept", # External dependencies from Maven "//dependencies/maven/artifacts/com/google/code/findbugs:jsr305", diff --git a/GraknClient.java b/GraknClient.java index 89084a3c8b..dc5e73879e 100644 --- a/GraknClient.java +++ b/GraknClient.java @@ -21,33 +21,34 @@ import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableList; -import grakn.client.concept.RemoteConcept; +import grakn.client.concept.Attribute; +import grakn.client.concept.AttributeType; +import grakn.client.concept.Concept; +import grakn.client.concept.ConceptId; +import grakn.client.concept.EntityType; +import grakn.client.concept.Label; +import grakn.client.concept.RelationType; +import grakn.client.concept.Role; +import grakn.client.concept.Rule; +import grakn.client.concept.SchemaConcept; +import grakn.client.answer.Answer; +import grakn.client.answer.AnswerGroup; +import grakn.client.answer.ConceptList; +import grakn.client.answer.ConceptMap; +import grakn.client.answer.ConceptSet; +import grakn.client.answer.ConceptSetMeasure; +import grakn.client.answer.Numeric; import grakn.client.exception.GraknClientException; import grakn.client.rpc.RequestBuilder; import grakn.client.rpc.ResponseReader; import grakn.client.rpc.Transceiver; -import grakn.core.concept.Concept; -import grakn.core.concept.ConceptId; -import grakn.core.concept.Label; -import grakn.core.concept.answer.AnswerGroup; -import grakn.core.concept.answer.ConceptList; -import grakn.core.concept.answer.ConceptMap; -import grakn.core.concept.answer.ConceptSet; -import grakn.core.concept.answer.ConceptSetMeasure; -import grakn.core.concept.answer.Numeric; -import grakn.core.concept.thing.Attribute; -import grakn.core.concept.type.AttributeType; -import grakn.core.concept.type.EntityType; -import grakn.core.concept.type.RelationType; -import grakn.core.concept.type.Role; -import grakn.core.concept.type.Rule; -import grakn.core.concept.type.SchemaConcept; import grakn.protocol.keyspace.KeyspaceProto; import grakn.protocol.keyspace.KeyspaceServiceGrpc; import grakn.protocol.keyspace.KeyspaceServiceGrpc.KeyspaceServiceBlockingStub; import grakn.protocol.session.ConceptProto; import grakn.protocol.session.SessionProto; import grakn.protocol.session.SessionServiceGrpc; +import graql.lang.Graql; import graql.lang.pattern.Pattern; import graql.lang.query.GraqlCompute; import graql.lang.query.GraqlDefine; @@ -64,21 +65,17 @@ import javax.annotation.Nullable; import java.io.Serializable; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toSet; - /** * Entry-point which communicates with a running Grakn server using gRPC. - * For now, only a subset of grakn.core.api.Session and grakn.core.api.Transaction features are supported. */ public class GraknClient implements AutoCloseable { @@ -110,7 +107,7 @@ public GraknClient overrideChannel(ManagedChannel channel) { return this; } - @Override + public void close() { channel.shutdown(); try { @@ -133,12 +130,10 @@ public Keyspaces keyspaces() { } /** - * Remote implementation of grakn.core.api.Session that communicates with a Grakn server using gRPC. - * * @see Transaction * @see GraknClient */ - public static class Session implements grakn.core.api.Session { + public static class Session { protected ManagedChannel channel; private String username; // TODO: Do we need to save this? It's not used. @@ -169,7 +164,6 @@ private Session(ManagedChannel channel, String username, String password, String isOpen = true; } - @Override public GraknClient.Transaction.Builder transaction() { return new Transaction.Builder(channel, this, sessionId); } @@ -178,28 +172,23 @@ public boolean isOpen() { return isOpen; } - @Override public void close() { if (!isOpen) return; sessionStub.close(RequestBuilder.Session.close(sessionId)); isOpen = false; } - @Override // TODO: remove this method once we no longer implement grakn.core.api.Session public Keyspace keyspace() { return Keyspace.of(keyspace); } } - /** - * Remote implementation of grakn.core.api.Transaction that communicates with a Grakn server using gRPC. - */ - public static class Transaction implements grakn.core.api.Transaction { + public static class Transaction implements AutoCloseable { private final Session session; private final Type type; private final Transceiver transceiver; - public static class Builder implements grakn.core.api.Transaction.Builder { + public static class Builder { private ManagedChannel channel; private GraknClient.Session session; @@ -211,12 +200,10 @@ public Builder(ManagedChannel channel, GraknClient.Session session, String sessi this.sessionId = sessionId; } - @Override public GraknClient.Transaction read() { return new GraknClient.Transaction(channel, session, sessionId, Transaction.Type.READ); } - @Override public GraknClient.Transaction write() { return new GraknClient.Transaction(channel, session, sessionId, Transaction.Type.WRITE); } @@ -257,50 +244,126 @@ private Transaction(ManagedChannel channel, Session session, String sessionId, T responseOrThrow(); } - @Override // TODO: Replace return type with GraknClient.Transaction.Type once we resolve graknlabs/grakn#5289 - public grakn.core.api.Transaction.Type type() { - return grakn.core.api.Transaction.Type.of(type.id()); + public Type type() { + return type; } - @Override public GraknClient.Session session() { return session; } - @Override public Keyspace keyspace() { return session.keyspace(); } - @Override + public List execute(GraqlDefine query) { + return stream(query).collect(Collectors.toList()); + } + + public List execute(GraqlUndefine query) { + return stream(query).collect(Collectors.toList()); + } + + public List execute(GraqlInsert query, boolean infer) { + return stream(query, infer).collect(Collectors.toList()); + } + public List execute(GraqlInsert query) { + return stream(query).collect(Collectors.toList()); + } + + public List execute(GraqlDelete query, boolean infer) { + return stream(query, infer).collect(Collectors.toList()); + } + public List execute(GraqlDelete query) { + return stream(query).collect(Collectors.toList()); + } + + public List execute(GraqlGet query, boolean infer) { + return stream(query, infer).collect(Collectors.toList()); + } + public List execute(GraqlGet query) { + return stream(query).collect(Collectors.toList()); + } + public Stream stream(GraqlDefine query) { Iterable iterable = () -> this.rpcIterator(query); return StreamSupport.stream(iterable.spliterator(), false); } - @Override public Stream stream(GraqlUndefine query) { Iterable iterable = () -> this.rpcIterator(query); return StreamSupport.stream(iterable.spliterator(), false); } - @Override public Stream stream(GraqlInsert query, boolean infer) { Iterable iterable = () -> this.rpcIterator(query, infer); return StreamSupport.stream(iterable.spliterator(), false); } + public Stream stream(GraqlInsert query) { + return stream(query, true); + } - @Override public Stream stream(GraqlDelete query, boolean infer) { Iterable iterable = () -> this.rpcIterator(query, infer); return StreamSupport.stream(iterable.spliterator(), false); } + public Stream stream(GraqlDelete query) { + return stream(query, true); + } - @Override public Stream stream(GraqlGet query, boolean infer) { Iterable iterable = () -> this.rpcIterator(query, infer); return StreamSupport.stream(iterable.spliterator(), false); } + public Stream stream(GraqlGet query) { + return stream(query, true); + } + + public Stream stream(GraqlQuery query) { + return stream(query, true); + } + + public Stream stream(GraqlQuery query, boolean infer) { + if (query instanceof GraqlDefine) { + return stream((GraqlDefine) query); + + } else if (query instanceof GraqlUndefine) { + return stream((GraqlUndefine) query); + + } else if (query instanceof GraqlInsert) { + return stream((GraqlInsert) query, infer); + + } else if (query instanceof GraqlDelete) { + return stream((GraqlDelete) query, infer); + + } else if (query instanceof GraqlGet) { + return stream((GraqlGet) query, infer); + + } else if (query instanceof GraqlGet.Aggregate) { + return stream((GraqlGet.Aggregate) query, infer); + + } else if (query instanceof GraqlGet.Group.Aggregate) { + return stream((GraqlGet.Group.Aggregate) query, infer); + + } else if (query instanceof GraqlGet.Group) { + return stream((GraqlGet.Group) query, infer); + + } else if (query instanceof GraqlCompute.Statistics) { + return stream((GraqlCompute.Statistics) query); + + } else if (query instanceof GraqlCompute.Path) { + return stream((GraqlCompute.Path) query); + + } else if (query instanceof GraqlCompute.Centrality) { + return stream((GraqlCompute.Centrality) query); + + } else if (query instanceof GraqlCompute.Cluster) { + return stream((GraqlCompute.Cluster) query); + + } else { + throw new IllegalArgumentException("Unrecognised Query object"); + } + } private Iterator rpcIterator(GraqlQuery query) { return rpcIterator(query, true); @@ -317,20 +380,23 @@ private Iterator rpcIterator(GraqlQuery query, boolean infer) { ); } - @Override - public void close() { - transceiver.close(); + public void abort() { + close(); } - @Override // TODO: Remove this method once we resolve graknlabs/grakn#5289 - public boolean isClosed() { - return !transceiver.isOpen(); + public void close() { + transceiver.close(); } public boolean isOpen() { return transceiver.isOpen(); } + // TODO remove - backwards compatibility + public boolean isClosed() { + return !isOpen(); + } + private SessionProto.Transaction.Res responseOrThrow() { Transceiver.Response response; @@ -357,7 +423,6 @@ private SessionProto.Transaction.Res responseOrThrow() { } } - @Override public void commit() { transceiver.send(RequestBuilder.Transaction.commit()); responseOrThrow(); @@ -365,15 +430,13 @@ public void commit() { } @Nullable - @Override - public T getType(Label label) { + public T getType(Label label) { SchemaConcept concept = getSchemaConcept(label); if (concept == null || !concept.isType()) return null; return (T) concept.asType(); } @Nullable - @Override public EntityType getEntityType(String label) { SchemaConcept concept = getSchemaConcept(Label.of(label)); if (concept == null || !concept.isEntityType()) return null; @@ -381,7 +444,6 @@ public EntityType getEntityType(String label) { } @Nullable - @Override public RelationType getRelationType(String label) { SchemaConcept concept = getSchemaConcept(Label.of(label)); if (concept == null || !concept.isRelationType()) return null; @@ -389,7 +451,6 @@ public RelationType getRelationType(String label) { } @Nullable - @Override public AttributeType getAttributeType(String label) { SchemaConcept concept = getSchemaConcept(Label.of(label)); if (concept == null || !concept.isAttributeType()) return null; @@ -397,7 +458,6 @@ public AttributeType getAttributeType(String label) { } @Nullable - @Override public Role getRole(String label) { SchemaConcept concept = getSchemaConcept(Label.of(label)); if (concept == null || !concept.isRole()) return null; @@ -405,7 +465,6 @@ public Role getRole(String label) { } @Nullable - @Override public Rule getRule(String label) { SchemaConcept concept = getSchemaConcept(Label.of(label)); if (concept == null || !concept.isRule()) return null; @@ -413,7 +472,6 @@ public Rule getRule(String label) { } @Nullable - @Override public T getSchemaConcept(Label label) { transceiver.send(RequestBuilder.Transaction.getSchemaConcept(label)); SessionProto.Transaction.Res response = responseOrThrow(); @@ -421,12 +479,15 @@ public T getSchemaConcept(Label label) { case NULL: return null; default: - return (T) RemoteConcept.of(response.getGetSchemaConceptRes().getSchemaConcept(), this).asSchemaConcept(); + return (T) Concept.of(response.getGetSchemaConceptRes().getSchemaConcept(), this).asSchemaConcept(); } } + public SchemaConcept getMetaConcept() { + return getSchemaConcept(Label.of(Graql.Token.Type.THING.toString())); + } + @Nullable - @Override public T getConcept(ConceptId id) { transceiver.send(RequestBuilder.Transaction.getConcept(id)); SessionProto.Transaction.Res response = responseOrThrow(); @@ -434,95 +495,93 @@ public T getConcept(ConceptId id) { case NULL: return null; default: - return (T) RemoteConcept.of(response.getGetConceptRes().getConcept(), this); + return (T) Concept.of(response.getGetConceptRes().getConcept(), this); } } - @Override public Collection> getAttributesByValue(V value) { transceiver.send(RequestBuilder.Transaction.getAttributes(value)); int iteratorId = responseOrThrow().getGetAttributesIter().getId(); - Iterable iterable = () -> new RPCIterator<>( - this, iteratorId, response -> RemoteConcept.of(response.getGetAttributesIterRes().getAttribute(), this) + Iterable> iterable = () -> new RPCIterator>( + this, iteratorId, response -> Concept.of(response.getGetAttributesIterRes().getAttribute(), this).asAttribute() ); - return StreamSupport.stream(iterable.spliterator(), false).map(Concept::asAttribute) - .collect(collectingAndThen(toSet(), Collections::unmodifiableSet)); + return StreamSupport.stream(iterable.spliterator(), false) + .collect(Collectors.toSet()); } - @Override public EntityType putEntityType(Label label) { transceiver.send(RequestBuilder.Transaction.putEntityType(label)); - return RemoteConcept.of(responseOrThrow().getPutEntityTypeRes().getEntityType(), this).asEntityType(); + return Concept.of(responseOrThrow().getPutEntityTypeRes().getEntityType(), this).asEntityType(); } - @Override + public AttributeType putAttributeType(Label label, AttributeType.DataType dataType) { transceiver.send(RequestBuilder.Transaction.putAttributeType(label, dataType)); - return RemoteConcept.of(responseOrThrow().getPutAttributeTypeRes().getAttributeType(), this).asAttributeType(); + return Concept.of(responseOrThrow().getPutAttributeTypeRes().getAttributeType(), this).asAttributeType(); } - @Override + public RelationType putRelationType(Label label) { transceiver.send(RequestBuilder.Transaction.putRelationType(label)); - return RemoteConcept.of(responseOrThrow().getPutRelationTypeRes().getRelationType(), this).asRelationType(); + return Concept.of(responseOrThrow().getPutRelationTypeRes().getRelationType(), this).asRelationType(); } - @Override + public Role putRole(Label label) { transceiver.send(RequestBuilder.Transaction.putRole(label)); - return RemoteConcept.of(responseOrThrow().getPutRoleRes().getRole(), this).asRole(); + return Concept.of(responseOrThrow().getPutRoleRes().getRole(), this).asRole(); } - @Override + public Rule putRule(Label label, Pattern when, Pattern then) { transceiver.send(RequestBuilder.Transaction.putRule(label, when, then)); - return RemoteConcept.of(responseOrThrow().getPutRuleRes().getRule(), this).asRule(); + return Concept.of(responseOrThrow().getPutRuleRes().getRule(), this).asRule(); } - @Override + public Stream stream(GraqlGet.Aggregate query, boolean infer) { Iterable iterable = () -> rpcIterator(query, infer); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream> stream(GraqlGet.Group query, boolean infer) { Iterable> iterable = () -> rpcIterator(query, infer); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream> stream(GraqlGet.Group.Aggregate query, boolean infer) { Iterable> iterable = () -> rpcIterator(query, infer); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream stream(GraqlCompute.Statistics query) { Iterable iterable = () -> rpcIterator(query, false); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream stream(GraqlCompute.Path query) { Iterable iterable = () -> rpcIterator(query, false); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream stream(GraqlCompute.Centrality query) { Iterable iterable = () -> rpcIterator(query, false); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream stream(GraqlCompute.Cluster query) { Iterable iterable = () -> rpcIterator(query, false); return StreamSupport.stream(iterable.spliterator(), false); } - @Override + public Stream sups(SchemaConcept schemaConcept) { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setSchemaConceptSupsReq(ConceptProto.SchemaConcept.Sups.Req.getDefaultInstance()).build(); @@ -531,7 +590,7 @@ public Stream sups(SchemaConcept schemaConcept) { int iteratorId = response.getConceptMethodRes().getResponse().getSchemaConceptSupsIter().getId(); Iterable iterable = () -> new RPCIterator<>( - this, iteratorId, res -> RemoteConcept.of(res.getConceptMethodIterRes().getSchemaConceptSupsIterRes().getSchemaConcept(), this) + this, iteratorId, res -> Concept.of(res.getConceptMethodIterRes().getSchemaConceptSupsIterRes().getSchemaConcept(), this) ); Stream sups = StreamSupport.stream(iterable.spliterator(), false); @@ -573,7 +632,7 @@ private RPCIterator(Transaction tx, int iteratorId, Function retrieve() { try { - KeyspaceProto.Keyspace.Retrieve.Req request = RequestBuilder.Keyspace.retrieve(this.username, this.password); + KeyspaceProto.Keyspace.Retrieve.Req request = RequestBuilder.KeyspaceMessage.retrieve(this.username, this.password); return ImmutableList.copyOf(keyspaceBlockingStub.retrieve(request).getNamesList().iterator()); } catch (StatusRuntimeException e) { throw GraknClientException.create(e.getMessage(), e); @@ -627,7 +686,7 @@ public List retrieve() { /** * An identifier for an isolated scope of a data in the database. */ - public static class Keyspace implements grakn.core.api.Keyspace, Serializable { + public static class Keyspace implements Serializable { private static final long serialVersionUID = 2726154016735929123L; public static final String DEFAULT = "grakn"; @@ -651,12 +710,10 @@ public String name() { return name; } - @Override public final String toString() { return name(); } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -665,7 +722,6 @@ public boolean equals(Object o) { return this.name.equals(that.name); } - @Override public int hashCode() { int h = 1; h *= 1000003; diff --git a/answer/Answer.java b/answer/Answer.java new file mode 100644 index 0000000000..e02345fd0a --- /dev/null +++ b/answer/Answer.java @@ -0,0 +1,51 @@ +/* + * 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 grakn.client.answer; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * An object that contains the answer of every Graql Query. + */ +public abstract class Answer { + + /** + * @return an explanation object indicating how this answer was obtained + */ + @Nullable + @CheckReturnValue + public abstract Explanation explanation(); + + /** + * @return all explanations taking part in the derivation of this answer + */ + @CheckReturnValue + public Set explanations() { + if (this.explanation() == null) return Collections.emptySet(); + Set explanations = new HashSet<>(); + explanations.add(this.explanation()); + this.explanation().getAnswers().forEach(ans -> explanations.addAll(ans.explanations())); + return explanations; + } +} diff --git a/answer/AnswerGroup.java b/answer/AnswerGroup.java new file mode 100644 index 0000000000..5d71c8e04f --- /dev/null +++ b/answer/AnswerGroup.java @@ -0,0 +1,79 @@ +/* + * 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 grakn.client.answer; + +import grakn.client.concept.Concept; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * A type of Answer object that contains a List of Answers as the members and a RemoteConcept + * as the owner. + * + * @param the type of Answer being grouped + */ +public class AnswerGroup extends Answer { + + private final Concept owner; + private final List answers; + private final Explanation explanation; + + public AnswerGroup(Concept owner, List answers) { + this(owner, answers, new Explanation()); + } + + public AnswerGroup(Concept owner, List answers, Explanation explanation) { + this.owner = owner; + this.answers = answers; + this.explanation = explanation; + } + + @Nullable + @Override + public Explanation explanation() { + return explanation; + } + + public Concept owner() { + return this.owner; + } + + public List answers() { + return this.answers; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || getClass() != obj.getClass()) return false; + AnswerGroup a2 = (AnswerGroup) obj; + return this.owner.equals(a2.owner) && + this.answers.equals(a2.answers); + } + + @Override + public int hashCode() { + int hash = owner.hashCode(); + hash = 31 * hash + answers.hashCode(); + + return hash; + } +} diff --git a/answer/ConceptList.java b/answer/ConceptList.java new file mode 100644 index 0000000000..e70ff815bc --- /dev/null +++ b/answer/ConceptList.java @@ -0,0 +1,62 @@ +/* + * 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 grakn.client.answer; + +import grakn.client.concept.ConceptId; + +import java.util.Collections; +import java.util.List; + +/** + * A type of Answer object that contains a List of Concepts. + */ +public class ConceptList extends Answer { + + // TODO: change to store List once we are able to construct Concept without a database look up + private final List list; + private final Explanation explanation; + + public ConceptList(List list) { + this.list = Collections.unmodifiableList(list); + this.explanation = new Explanation(); + } + + @Override + public Explanation explanation() { + return explanation; + } + + public List list() { + return list; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ConceptList a2 = (ConceptList) obj; + return this.list.equals(a2.list); + } + + @Override + public int hashCode() { + return list.hashCode(); + } +} diff --git a/answer/ConceptMap.java b/answer/ConceptMap.java new file mode 100644 index 0000000000..946c871140 --- /dev/null +++ b/answer/ConceptMap.java @@ -0,0 +1,82 @@ +/* + * 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 grakn.client.answer; + +import grakn.client.concept.Concept; +import grakn.client.concept.GraknConceptException; +import graql.lang.statement.Variable; + +import javax.annotation.CheckReturnValue; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * A type of Answer object that contains a Map of Concepts. + */ +public class ConceptMap extends Answer { + + private final Map map; + private final Explanation explanation; + + public ConceptMap(Map map, Explanation exp) { + this.map = Collections.unmodifiableMap(map); + this.explanation = exp; + } + + @Override + public Explanation explanation() { + return explanation; + } + + @CheckReturnValue + public Map map() { + return map; + } + + + @CheckReturnValue + public Concept get(String variable) { + Variable var = new Variable(variable); + Concept Concept = map.get(var); + if (Concept == null) throw GraknConceptException.variableDoesNotExist(var.toString()); + return Concept; + } + + @Override + public String toString() { + return map.entrySet().stream() + .sorted(Comparator.comparing(e -> e.getKey().name())) + .map(e -> "[" + e.getKey() + "/" + e.getValue().id() + "]").collect(Collectors.joining()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ConceptMap a2 = (ConceptMap) obj; + return map.equals(a2.map); + } + + @Override + public int hashCode() { return map.hashCode();} +} diff --git a/answer/ConceptSet.java b/answer/ConceptSet.java new file mode 100644 index 0000000000..2d2fb6620d --- /dev/null +++ b/answer/ConceptSet.java @@ -0,0 +1,65 @@ +/* + * 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 grakn.client.answer; + +import grakn.client.concept.ConceptId; + +import java.util.Collections; +import java.util.Set; + +/** + * A type of Answer object that contains a Set. + */ +public class ConceptSet extends Answer { + + // TODO: change to store Set once we are able to construct Concept without a database look up + private final Set set; + private final Explanation explanation; + + public ConceptSet(Set set) { + this(set, new Explanation()); + } + + public ConceptSet(Set set, Explanation explanation) { + this.set = Collections.unmodifiableSet(set); + this.explanation = explanation; + } + + @Override + public Explanation explanation() { + return explanation; + } + + public Set set() { + return set; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ConceptSet a2 = (ConceptSet) obj; + return this.set.equals(a2.set); + } + + @Override + public int hashCode() { + return set.hashCode(); + } +} diff --git a/answer/ConceptSetMeasure.java b/answer/ConceptSetMeasure.java new file mode 100644 index 0000000000..1965d934a8 --- /dev/null +++ b/answer/ConceptSetMeasure.java @@ -0,0 +1,62 @@ +/* + * 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 grakn.client.answer; + +import grakn.client.concept.ConceptId; + +import java.util.Set; + +/** + * A type of Answer object that contains a Set and Number, by extending RemoteConceptSet. + */ +public class ConceptSetMeasure extends ConceptSet { + + private final Number measurement; + + public ConceptSetMeasure(Set set, Number measurement) { + this(set, measurement, new Explanation()); + } + + public ConceptSetMeasure(Set set, Number measurement, Explanation explanation) { + super(set, explanation); + this.measurement = measurement; + } + + public Number measurement() { + return measurement; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ConceptSetMeasure a2 = (ConceptSetMeasure) obj; + return this.set().equals(a2.set()) + && measurement.toString().equals(a2.measurement.toString()); + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 31 * hash + measurement.hashCode(); + + return hash; + } +} diff --git a/answer/Explanation.java b/answer/Explanation.java new file mode 100644 index 0000000000..a2a8475120 --- /dev/null +++ b/answer/Explanation.java @@ -0,0 +1,59 @@ +/* + * 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 grakn.client.answer; + +import graql.lang.pattern.Pattern; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +/** + * Reasoner explanation for inferred answers + */ +public class Explanation { + + private final Pattern pattern; + private final List answers; + + public Explanation() { + this.pattern = null; + this.answers = Collections.unmodifiableList(Collections.emptyList()); + } + + public Explanation(Pattern pattern, List ans) { + this.pattern = pattern; + this.answers = Collections.unmodifiableList(ans); + } + + /** + * @return query pattern associated with this explanation + */ + @CheckReturnValue + @Nullable + public Pattern getPattern() { return pattern;} + + /** + * @return answers this explanation is dependent on + */ + @CheckReturnValue + public List getAnswers() { return answers;} +} diff --git a/answer/Numeric.java b/answer/Numeric.java new file mode 100644 index 0000000000..4c5e02ce71 --- /dev/null +++ b/answer/Numeric.java @@ -0,0 +1,60 @@ +/* + * 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 grakn.client.answer; + +/** + * A type of Answer object that contains a Number. + */ +public class Numeric extends Answer { + + private final Number number; + private final Explanation explanation; + + public Numeric(Number number) { + this(number, new Explanation()); + } + + public Numeric(Number number, Explanation explanation) { + this.number = number; + this.explanation = explanation; + } + + @Override + public Explanation explanation() { + return explanation; + } + + public Number number() { + return number; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Numeric a2 = (Numeric) obj; + return this.number.toString().equals(a2.number.toString()); + } + + @Override + public int hashCode() { + return number.hashCode(); + } +} diff --git a/concept/RemoteAttribute.java b/concept/Attribute.java similarity index 83% rename from concept/RemoteAttribute.java rename to concept/Attribute.java index 549b7222aa..afa7c135d4 100644 --- a/concept/RemoteAttribute.java +++ b/concept/Attribute.java @@ -20,13 +20,9 @@ package grakn.client.concept; import grakn.client.GraknClient; -import grakn.core.concept.Concept; -import grakn.core.concept.ConceptId; -import grakn.core.concept.thing.Attribute; -import grakn.core.concept.thing.Thing; -import grakn.core.concept.type.AttributeType; import grakn.protocol.session.ConceptProto; +import javax.annotation.CheckReturnValue; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -37,17 +33,12 @@ * * @param The data type of this attribute */ -public class RemoteAttribute extends RemoteThing, AttributeType> implements Attribute { +public class Attribute extends Thing> { - RemoteAttribute(GraknClient.Transaction tx, ConceptId id) { + Attribute(GraknClient.Transaction tx, ConceptId id) { super(tx, id); } - static RemoteAttribute construct(GraknClient.Transaction tx, ConceptId id) { - return new RemoteAttribute<>(tx, id); - } - - @Override public final D value() { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeValueReq(ConceptProto.Attribute.Value.Req.getDefaultInstance()).build(); @@ -80,7 +71,6 @@ private D castValue(ConceptProto.ValueObject value) { } } - @Override public final Stream owners() { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeOwnersReq(ConceptProto.Attribute.Owners.Req.getDefaultInstance()).build(); @@ -89,18 +79,30 @@ public final Stream owners() { return conceptStream(iteratorId, res -> res.getAttributeOwnersIterRes().getThing()).map(Concept::asThing); } - @Override public final AttributeType.DataType dataType() { return type().dataType(); } - @Override final AttributeType asCurrentType(Concept concept) { return concept.asAttributeType(); } - @Override - final Attribute asCurrentBaseType(Concept other) { + final Attribute asCurrentBaseType(Concept other) { return other.asAttribute(); } + + @SuppressWarnings("unchecked") + @Deprecated + @CheckReturnValue + @Override + public Attribute asAttribute() { + return this; + } + + @Deprecated + @CheckReturnValue + @Override + public boolean isAttribute() { + return true; + } } diff --git a/concept/RemoteAttributeType.java b/concept/AttributeType.java similarity index 57% rename from concept/RemoteAttributeType.java rename to concept/AttributeType.java index cd07eb44b8..4699fb017d 100644 --- a/concept/RemoteAttributeType.java +++ b/concept/AttributeType.java @@ -22,59 +22,50 @@ import grakn.client.GraknClient; import grakn.client.exception.GraknClientException; import grakn.client.rpc.RequestBuilder; -import grakn.core.concept.Concept; -import grakn.core.concept.ConceptId; -import grakn.core.concept.thing.Attribute; -import grakn.core.concept.type.AttributeType; import grakn.protocol.session.ConceptProto; +import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; +import java.time.LocalDateTime; /** * Client implementation of AttributeType * * @param The data type of this attribute type */ -public class RemoteAttributeType extends RemoteType, Attribute> implements AttributeType { +public class AttributeType extends Type> { - RemoteAttributeType(GraknClient.Transaction tx, ConceptId id) { + AttributeType(GraknClient.Transaction tx, ConceptId id) { super(tx, id); } - static RemoteAttributeType construct(GraknClient.Transaction tx, ConceptId id) { - return new RemoteAttributeType<>(tx, id); - } - - @Override public final Attribute create(D value) { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeTypeCreateReq(ConceptProto.AttributeType.Create.Req.newBuilder() - .setValue(RequestBuilder.Concept.attributeValue(value))).build(); + .setValue(RequestBuilder.ConceptMessage.attributeValue(value))).build(); - Concept concept = RemoteConcept.of(runMethod(method).getAttributeTypeCreateRes().getAttribute(), tx()); + Concept concept = Concept.of(runMethod(method).getAttributeTypeCreateRes().getAttribute(), tx()); return asInstance(concept); } @Nullable - @Override public final Attribute attribute(D value) { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeTypeAttributeReq(ConceptProto.AttributeType.Attribute.Req.newBuilder() - .setValue(RequestBuilder.Concept.attributeValue(value))).build(); + .setValue(RequestBuilder.ConceptMessage.attributeValue(value))).build(); ConceptProto.AttributeType.Attribute.Res response = runMethod(method).getAttributeTypeAttributeRes(); switch (response.getResCase()) { case NULL: return null; case ATTRIBUTE: - return RemoteConcept.of(response.getAttribute(), tx()).asAttribute(); + return Concept.of(response.getAttribute(), tx()).asAttribute(); default: throw GraknClientException.unreachableStatement("Unexpected response " + response); } } @Nullable - @Override public final AttributeType.DataType dataType() { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeTypeDataTypeReq(ConceptProto.AttributeType.DataType.Req.getDefaultInstance()).build(); @@ -84,14 +75,13 @@ public final AttributeType.DataType dataType() { case NULL: return null; case DATATYPE: - return (AttributeType.DataType) RequestBuilder.Concept.dataType(response.getDataType()); + return (AttributeType.DataType) RequestBuilder.ConceptMessage.dataType(response.getDataType()); default: throw GraknClientException.unreachableStatement("Unexpected response " + response); } } @Nullable - @Override public final String regex() { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeTypeGetRegexReq(ConceptProto.AttributeType.GetRegex.Req.getDefaultInstance()).build(); @@ -100,8 +90,7 @@ public final String regex() { return regex.isEmpty() ? null : regex; } - @Override - public final AttributeType regex(String regex) { + public final AttributeType regex(String regex) { if (regex == null) regex = ""; ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setAttributeTypeSetRegexReq(ConceptProto.AttributeType.SetRegex.Req.newBuilder() @@ -112,7 +101,7 @@ public final AttributeType regex(String regex) { } @Override - final AttributeType asCurrentBaseType(Concept other) { + final AttributeType asCurrentBaseType(Concept other) { return other.asAttributeType(); } @@ -125,4 +114,75 @@ final boolean equalsCurrentBaseType(Concept other) { protected final Attribute asInstance(Concept concept) { return concept.asAttribute(); } + + @SuppressWarnings("unchecked") + @Deprecated + @CheckReturnValue + @Override + public AttributeType asAttributeType() { + return this; + } + + @Deprecated + @CheckReturnValue + @Override + public boolean isAttributeType() { + return true; + } + + + /** + * A class used to hold the supported data types of resources and any other concepts. + * This is used tp constrain value data types to only those we explicitly support. + * + * @param The data type. + */ + public static class DataType { + public static final AttributeType.DataType BOOLEAN = new DataType<>(Boolean.class); + public static final AttributeType.DataType DATE = new AttributeType.DataType<>(LocalDateTime.class); + public static final AttributeType.DataType DOUBLE = new AttributeType.DataType<>(Double.class); + public static final AttributeType.DataType FLOAT = new AttributeType.DataType<>(Float.class); + public static final AttributeType.DataType INTEGER = new AttributeType.DataType<>(Integer.class); + public static final AttributeType.DataType LONG = new AttributeType.DataType<>(Long.class); + public static final AttributeType.DataType STRING = new AttributeType.DataType<>(String.class); + + private final Class dataClass; + + private DataType(Class dataClass) { + this.dataClass = dataClass; + } + + @CheckReturnValue + public Class dataClass() { + return dataClass; + } + + @CheckReturnValue + public String name() { + return dataClass.getName(); + } + + @Override + public String toString() { + return name(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AttributeType.DataType that = (AttributeType.DataType) o; + + return (this.dataClass().equals(that.dataClass())); + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= dataClass.hashCode(); + return h; + } + } } diff --git a/concept/Concept.java b/concept/Concept.java new file mode 100644 index 0000000000..e97df2a3e3 --- /dev/null +++ b/concept/Concept.java @@ -0,0 +1,366 @@ +/* + * 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 grakn.client.concept; + +import grakn.client.GraknClient; +import grakn.protocol.session.ConceptProto; + +import javax.annotation.CheckReturnValue; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * Client implementation of Concept + * + * @param represents the actual class of object to downcast to + */ +public abstract class Concept { + + private final GraknClient.Transaction tx; + private final ConceptId id; + + Concept(GraknClient.Transaction tx, ConceptId id) { + if (tx == null) { + throw new NullPointerException("Null tx"); + } + this.tx = tx; + if (id == null) { + throw new NullPointerException("Null id"); + } + this.id = id; + } + + public static Concept of(ConceptProto.Concept concept, GraknClient.Transaction tx) { + ConceptId id = ConceptId.of(concept.getId()); + + switch (concept.getBaseType()) { + case ENTITY: + return new Entity(tx, id); + case RELATION: + return new Relation(tx, id); + case ATTRIBUTE: + return new Attribute(tx, id); + case ENTITY_TYPE: + return new EntityType(tx, id); + case RELATION_TYPE: + return new RelationType(tx, id); + case ATTRIBUTE_TYPE: + return new AttributeType(tx, id); + case ROLE: + return new Role(tx, id); + case RULE: + return new Rule(tx, id); + case META_TYPE: + return new MetaType(tx, id); + default: + case UNRECOGNIZED: + throw new IllegalArgumentException("Unrecognised " + concept); + } + } + + + public ConceptId id() { + return id; + } + + public final void delete() { + ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() + .setConceptDeleteReq(ConceptProto.Concept.Delete.Req.getDefaultInstance()) + .build(); + + runMethod(method); + } + + public final boolean isDeleted() { + return tx().getConcept(id()) == null; + } + + /** + * Return as a SchemaConcept if the Concept is a SchemaConcept. + * + * @return A SchemaConcept if the Concept is a SchemaConcept + */ + @CheckReturnValue + public SchemaConcept asSchemaConcept() { + throw GraknConceptException.invalidCasting(this, SchemaConcept.class); + } + + /** + * Return as a Type if the Concept is a Type. + * + * @return A Type if the Concept is a Type + */ + @CheckReturnValue + public Type asType() { + throw GraknConceptException.invalidCasting(this, Type.class); + } + + /** + * Return as an Thing if the Concept is an Thing. + * + * @return An Thing if the Concept is an Thing + */ + @CheckReturnValue + public Thing asThing() { + throw GraknConceptException.invalidCasting(this, Thing.class); + } + + /** + * Return as an EntityType if the Concept is an EntityType. + * + * @return A EntityType if the Concept is an EntityType + */ + @CheckReturnValue + public EntityType asEntityType() { + throw GraknConceptException.invalidCasting(this, EntityType.class); + } + + /** + * Return as a Role if the Concept is a Role. + * + * @return A Role if the Concept is a Role + */ + @CheckReturnValue + public Role asRole() { + throw GraknConceptException.invalidCasting(this, Role.class); + } + + /** + * Return as a RelationType if the Concept is a RelationType. + * + * @return A RelationType if the Concept is a RelationType + */ + @CheckReturnValue + public RelationType asRelationType() { + throw GraknConceptException.invalidCasting(this, RelationType.class); + } + + /** + * Return as a AttributeType if the Concept is a AttributeType + * + * @return A AttributeType if the Concept is a AttributeType + */ + @CheckReturnValue + public AttributeType asAttributeType() { + throw GraknConceptException.invalidCasting(this, AttributeType.class); + } + + /** + * Return as a Rule if the Concept is a Rule. + * + * @return A Rule if the Concept is a Rule + */ + @CheckReturnValue + public Rule asRule() { + throw GraknConceptException.invalidCasting(this, Rule.class); + } + + /** + * Return as an Entity, if the Concept is an Entity Thing. + * + * @return An Entity if the Concept is a Thing + */ + @CheckReturnValue + public Entity asEntity() { + throw GraknConceptException.invalidCasting(this, Entity.class); + } + + /** + * Return as a Relation if the Concept is a Relation Thing. + * + * @return A Relation if the Concept is a Relation + */ + @CheckReturnValue + public Relation asRelation() { + throw GraknConceptException.invalidCasting(this, Relation.class); + } + + /** + * Return as a Attribute if the Concept is a Attribute Thing. + * + * @return A Attribute if the Concept is a Attribute + */ + @CheckReturnValue + public Attribute asAttribute() { + throw GraknConceptException.invalidCasting(this, Attribute.class); + } + + /** + * Determine if the Concept is a SchemaConcept + * + * @return true if theConcept concept is a SchemaConcept + */ + @CheckReturnValue + public boolean isSchemaConcept() { + return false; + } + + /** + * Determine if the Concept is a Type. + * + * @return true if theConcept concept is a Type + */ + @CheckReturnValue + public boolean isType() { + return false; + } + + /** + * Determine if the Concept is an Thing. + * + * @return true if the Concept is an Thing + */ + @CheckReturnValue + public boolean isThing() { + return false; + } + + /** + * Determine if the Concept is an EntityType. + * + * @return true if the Concept is an EntityType. + */ + @CheckReturnValue + public boolean isEntityType() { + return false; + } + + /** + * Determine if the Concept is a Role. + * + * @return true if the Concept is a Role + */ + @CheckReturnValue + public boolean isRole() { + return false; + } + + /** + * Determine if the Concept is a RelationType. + * + * @return true if the Concept is a RelationType + */ + @CheckReturnValue + public boolean isRelationType() { + return false; + } + + /** + * Determine if the Concept is a AttributeType. + * + * @return true if theConcept concept is a AttributeType + */ + @CheckReturnValue + public boolean isAttributeType() { + return false; + } + + /** + * Determine if the Concept is a Rule. + * + * @return true if the Concept is a Rule + */ + @CheckReturnValue + public boolean isRule() { + return false; + } + + /** + * Determine if the Concept is an Entity. + * + * @return true if the Concept is a Entity + */ + @CheckReturnValue + public boolean isEntity() { + return false; + } + + /** + * Determine if the Concept is a Relation. + * + * @return true if the Concept is a Relation + */ + @CheckReturnValue + public boolean isRelation() { + return false; + } + + /** + * Determine if the Concept is a Attribute. + * + * @return true if the Concept is a Attribute + */ + @CheckReturnValue + public boolean isAttribute() { + return false; + } + + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{tx=" + tx + ", id=" + id + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Concept that = (Concept) o; + + return (tx.equals(that.tx())) && + id.equals(that.id()); + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= tx.hashCode(); + h *= 1000003; + h ^= id.hashCode(); + return h; + } + + GraknClient.Transaction tx() { + return tx; + } + abstract SomeConcept asCurrentBaseType(Concept other); + + final Stream conceptStream + (int iteratorId, Function conceptGetter) { + + Iterable iterable = () -> tx().iterator( + iteratorId, res -> of(conceptGetter.apply(res.getConceptMethodIterRes()), tx()) + ); + + return StreamSupport.stream(iterable.spliterator(), false); + } + + protected final ConceptProto.Method.Res runMethod(ConceptProto.Method.Req method) { + return runMethod(id(), method); + } + + protected final ConceptProto.Method.Res runMethod(ConceptId id, ConceptProto.Method.Req method) { + return tx().runConceptMethod(id, method).getConceptMethodRes().getResponse(); + } + +} diff --git a/concept/ConceptId.java b/concept/ConceptId.java new file mode 100644 index 0000000000..77883b6f14 --- /dev/null +++ b/concept/ConceptId.java @@ -0,0 +1,92 @@ +/* + * 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 grakn.client.concept; + +import javax.annotation.CheckReturnValue; +import java.io.Serializable; + +/** + * A class which represents an id of any Concept. + * Also contains a static method for producing concept IDs from Strings. + */ +public class ConceptId implements Comparable, Serializable { + + private static final long serialVersionUID = -1723590529071614152L; + private final String value; + + /** + * A non-argument constructor for ConceptID, for serialisation of OLAP queries dependencies + */ + ConceptId() { + this.value = null; + } + + /** + * The default constructor for ConceptID, which requires String value provided + * + * @param value String representation of the Concept ID + */ + ConceptId(String value) { + if (value == null) throw new NullPointerException("Provided ConceptId is NULL"); + + this.value = value; + } + + /** + * @param value The string which potentially represents a Concept + * @return The matching concept ID + */ + @CheckReturnValue + public static ConceptId of(String value) { + return new ConceptId(value); + } + + /** + * @return Used for indexing purposes and for graql traversals + */ + @CheckReturnValue + public String getValue() { + return value; + } + + @Override + public int compareTo(ConceptId o) { + return getValue().compareTo(o.getValue()); + } + + @Override + public final String toString() { + return value; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || this.getClass() != o.getClass()) return false; + + ConceptId that = (ConceptId) o; + return (this.value.equals(that.getValue())); + } + + @Override + public int hashCode() { + int result = 31 * this.value.hashCode(); + return result; + } +} diff --git a/concept/RemoteEntity.java b/concept/Entity.java similarity index 73% rename from concept/RemoteEntity.java rename to concept/Entity.java index 9630ceedc2..791453d6ea 100644 --- a/concept/RemoteEntity.java +++ b/concept/Entity.java @@ -20,22 +20,30 @@ package grakn.client.concept; import grakn.client.GraknClient; -import grakn.core.concept.Concept; -import grakn.core.concept.ConceptId; -import grakn.core.concept.thing.Entity; -import grakn.core.concept.type.EntityType; + +import javax.annotation.CheckReturnValue; /** * Client implementation of Entity */ -public class RemoteEntity extends RemoteThing implements Entity { +public class Entity extends Thing { - RemoteEntity(GraknClient.Transaction tx, ConceptId id) { + Entity(GraknClient.Transaction tx, ConceptId id) { super(tx, id); } - static RemoteEntity construct(GraknClient.Transaction tx, ConceptId id) { - return new RemoteEntity(tx, id); + @Deprecated + @CheckReturnValue + @Override + public Entity asEntity() { + return this; + } + + @Deprecated + @CheckReturnValue + @Override + public boolean isEntity() { + return true; } @Override diff --git a/concept/RemoteEntityType.java b/concept/EntityType.java similarity index 76% rename from concept/RemoteEntityType.java rename to concept/EntityType.java index 3b0eaa8a9d..b60a7e0405 100644 --- a/concept/RemoteEntityType.java +++ b/concept/EntityType.java @@ -20,33 +20,26 @@ package grakn.client.concept; import grakn.client.GraknClient; -import grakn.core.concept.Concept; -import grakn.core.concept.ConceptId; -import grakn.core.concept.thing.Entity; -import grakn.core.concept.type.EntityType; import grakn.protocol.session.ConceptProto; +import javax.annotation.CheckReturnValue; + /** * Client implementation of a MetaType, a special type of Type * TODO: This class is not defined in Concept API, and at server side implementation. * TODO: we should remove this class, or implement properly on server side. */ -public class RemoteEntityType extends RemoteType implements EntityType { +public class EntityType extends Type { - RemoteEntityType(GraknClient.Transaction tx, ConceptId id) { + public EntityType(GraknClient.Transaction tx, ConceptId id) { super(tx, id); } - static RemoteEntityType construct(GraknClient.Transaction tx, ConceptId id) { - return new RemoteEntityType(tx, id); - } - - @Override public final Entity create() { ConceptProto.Method.Req method = ConceptProto.Method.Req.newBuilder() .setEntityTypeCreateReq(ConceptProto.EntityType.Create.Req.getDefaultInstance()).build(); - Concept concept = RemoteConcept.of(runMethod(method).getEntityTypeCreateRes().getEntity(), tx()); + Concept concept = Concept.of(runMethod(method).getEntityTypeCreateRes().getEntity(), tx()); return asInstance(concept); } @@ -64,4 +57,18 @@ final boolean equalsCurrentBaseType(Concept other) { protected final Entity asInstance(Concept concept) { return concept.asEntity(); } + + @Deprecated + @CheckReturnValue + @Override + public EntityType asEntityType() { + return this; + } + + @Deprecated + @CheckReturnValue + @Override + public boolean isEntityType() { + return true; + } } diff --git a/concept/GraknConceptException.java b/concept/GraknConceptException.java new file mode 100644 index 0000000000..d1ffe20666 --- /dev/null +++ b/concept/GraknConceptException.java @@ -0,0 +1,48 @@ +/* + * 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 grakn.client.concept; + +public class GraknConceptException extends RuntimeException { + final static String INVALID_OBJECT_TYPE = "The concept [%s] is not of type [%s]"; + final static String VARIABLE_DOES_NOT_EXIST = "the variable [%s] does not exist"; + + private GraknConceptException(String error) { + super(error); + } + + public String getName() { + return this.getClass().getName(); + } + + public static GraknConceptException create(String error) { + return new GraknConceptException(error); + } + + /** + * Thrown when casting Grakn concepts/answers incorrectly. + */ + public static GraknConceptException invalidCasting(Object concept, Class type) { + return GraknConceptException.create(String.format(INVALID_OBJECT_TYPE,concept, type)); + } + + public static GraknConceptException variableDoesNotExist(String var) { + return new GraknConceptException(String.format(VARIABLE_DOES_NOT_EXIST, var)); + } +} diff --git a/concept/Label.java b/concept/Label.java new file mode 100644 index 0000000000..560c885281 --- /dev/null +++ b/concept/Label.java @@ -0,0 +1,96 @@ +/* + * 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 grakn.client.concept; + +import javax.annotation.CheckReturnValue; +import java.io.Serializable; +import java.util.function.Function; + +/** + * A Label + * A class which represents the unique label of any SchemaConcept + * Also contains a static method for producing Labels from Strings. + */ +public class Label implements Comparable