View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.rdf.rdf4j.impl;
19  
20  import java.nio.charset.StandardCharsets;
21  import java.util.UUID;
22  
23  import org.apache.commons.rdf.api.BlankNode;
24  import org.apache.commons.rdf.rdf4j.RDF4JBlankNode;
25  import org.eclipse.rdf4j.model.BNode;
26  import org.eclipse.rdf4j.rio.turtle.TurtleUtil;
27  
28  final class BlankNodeImpl extends AbstractRDFTerm<BNode> implements RDF4JBlankNode {
29  
30      private transient int hashCode = 0;
31      private final long saltUUIDleast;
32      private final long saltUUIDmost;
33  
34      BlankNodeImpl(final BNode bNode, final UUID salt) {
35          super(bNode);
36          // Space-efficient storage of salt UUID
37          saltUUIDmost = salt.getMostSignificantBits();
38          saltUUIDleast = salt.getLeastSignificantBits();
39      }
40  
41      @Override
42      public boolean equals(final Object obj) {
43          if (obj == this) {
44              return true;
45          }
46          // NOTE: Do NOT use Bnode.equals() as it has a more generous
47          // equality based only on the value.getID();
48          if (obj instanceof BlankNode) {
49              final BlankNode blankNode = (BlankNode) obj;
50              return uniqueReference().equals(blankNode.uniqueReference());
51          }
52          return false;
53      }
54  
55      @Override
56      public int hashCode() {
57          if (hashCode != 0) {
58              return hashCode;
59          }
60          return hashCode = uniqueReference().hashCode();
61      }
62  
63      private boolean isValidBlankNodeLabel(final String id) {
64          // FIXME: Replace with a regular expression?
65          if (id.isEmpty()) {
66              return false;
67          }
68          if (!TurtleUtil.isBLANK_NODE_LABEL_StartChar(id.codePointAt(0))) {
69              return false;
70          }
71          for (int i = 1; i < id.length(); i++) {
72              if (!TurtleUtil.isBLANK_NODE_LABEL_Char(id.codePointAt(i))) {
73                  return false;
74              }
75          }
76          return true;
77      }
78  
79      @Override
80      public String ntriplesString() {
81          if (isValidBlankNodeLabel(value.getID())) {
82              return "_:" + value.getID();
83          }
84          return "_:" + UUID.nameUUIDFromBytes(value.getID().getBytes(StandardCharsets.UTF_8));
85      }
86  
87      @Override
88      public String uniqueReference() {
89          final UUID uuid = new UUID(saltUUIDmost, saltUUIDleast);
90          return "urn:uuid:" + uuid + "#" + value.getID();
91      }
92  
93      @Override
94      public String toString() {
95          return ntriplesString() + " [" + uniqueReference() + "]";
96      }
97  }