1 /*
2  * This file is part of ELKI:
3  * Environment for Developing KDD-Applications Supported by Index-Structures
4  *
5  * Copyright (C) 2018
6  * ELKI Development Team
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 package de.lmu.ifi.dbs.elki.database.ids.integer;
22 
23 import java.io.IOException;
24 import java.nio.ByteBuffer;
25 
26 import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
27 import de.lmu.ifi.dbs.elki.database.ids.DBID;
28 import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
29 import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
30 import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
31 import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
32 import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
33 import de.lmu.ifi.dbs.elki.utilities.io.ByteArrayUtil;
34 import de.lmu.ifi.dbs.elki.utilities.io.ByteBufferSerializer;
35 import de.lmu.ifi.dbs.elki.utilities.io.FixedSizeByteBufferSerializer;
36 
37 /**
38  * Database ID object.
39  *
40  * While this currently is just an Integer, it should be avoided to store the
41  * object IDs in regular integers to reduce problems if this API ever changes
42  * (for example if someone needs to support {@code long}, it should not require
43  * changes in too many places!)
44  *
45  * In particular, a developer should not make any assumption of these IDs being
46  * consistent across multiple results/databases.
47  *
48  * @author Erich Schubert
49  * @since 0.4.0
50  *
51  * @composed - - - DynamicSerializer
52  * @composed - - - StaticSerializer
53  */
54 final class IntegerDBID implements DBID, IntegerDBIDRef {
55   /**
56    * The actual object ID.
57    */
58   protected final int id;
59 
60   /**
61    * Constructor from integer id.
62    *
63    * @param id integer id.
64    */
IntegerDBID(int id)65   protected IntegerDBID(int id) {
66     super();
67     this.id = id;
68   }
69 
70   /**
71    * Constructor from integer id.
72    *
73    * @param id integer id.
74    */
IntegerDBID(Integer id)75   protected IntegerDBID(Integer id) {
76     super();
77     this.id = id.intValue();
78   }
79 
80   /**
81    * Return the integer value of the object ID.
82    *
83    * @return integer id
84    */
85   @Override
internalGetIndex()86   public int internalGetIndex() {
87     return this.id;
88   }
89 
90   @Override
size()91   public int size() {
92     return 1;
93   }
94 
95   @Override
isEmpty()96   public boolean isEmpty() {
97     return false;
98   }
99 
100   @Override
toString()101   public String toString() {
102     return (id != Integer.MIN_VALUE) ? Integer.toString(id) : "null";
103   }
104 
105   @Override
hashCode()106   public int hashCode() {
107     return id;
108   }
109 
110   @Override
111   @Deprecated
equals(Object obj)112   public boolean equals(Object obj) {
113     if(this == obj) {
114       return true;
115     }
116     if(!(obj instanceof IntegerDBID)) {
117       if(obj instanceof DBIDRef) {
118         LoggingUtil.warning("Programming error: DBID.equals(DBIDRef) is not well-defined. Use DBIDUtil.equal() instead!", new Throwable());
119       }
120       return false;
121     }
122     IntegerDBID other = (IntegerDBID) obj;
123     return this.id == other.id;
124   }
125 
126   @Override
compareTo(DBIDRef o)127   public int compareTo(DBIDRef o) {
128     final int anotherVal = o.internalGetIndex();
129     return (this.id < anotherVal ? -1 : (this.id == anotherVal ? 0 : 1));
130   }
131 
132   @Override
iter()133   public Itr iter() {
134     return new Itr();
135   }
136 
137   @Override
get(int i)138   public DBID get(int i) {
139     if(i != 0) {
140       throw new ArrayIndexOutOfBoundsException();
141     }
142     return this;
143   }
144 
145   @Override
assignVar(int index, DBIDVar var)146   public DBIDVar assignVar(int index, DBIDVar var) {
147     if(index != 0) {
148       throw new ArrayIndexOutOfBoundsException();
149     }
150     var.set(this);
151     return var;
152   }
153 
154   @Override
contains(DBIDRef o)155   public boolean contains(DBIDRef o) {
156     return o.internalGetIndex() == id;
157   }
158 
159   @Override
binarySearch(DBIDRef key)160   public int binarySearch(DBIDRef key) {
161     final int other = key.internalGetIndex();
162     return (other == id) ? 0 : (other < id) ? -1 : -2;
163   }
164 
165   @Override
slice(int begin, int end)166   public ArrayDBIDs slice(int begin, int end) {
167     if(begin == 0 && end == 1) {
168       return this;
169     }
170     else {
171       return DBIDUtil.EMPTYDBIDS;
172     }
173   }
174 
175   /**
176    * Pseudo iterator for DBIDs interface.
177    *
178    * @author Erich Schubert
179    */
180   protected class Itr implements DBIDArrayIter, IntegerDBIDRef {
181     /**
182      * Iterator position: We use an integer so we can support retract().
183      */
184     int pos = 0;
185 
186     @Override
advance()187     public Itr advance() {
188       pos++;
189       return this;
190     }
191 
192     @Override
advance(int count)193     public Itr advance(int count) {
194       pos += count;
195       return this;
196     }
197 
198     @Override
retract()199     public Itr retract() {
200       pos--;
201       return this;
202     }
203 
204     @Override
seek(int off)205     public Itr seek(int off) {
206       pos = off;
207       return this;
208     }
209 
210     @Override
getOffset()211     public int getOffset() {
212       return pos;
213     }
214 
215     @Override
internalGetIndex()216     public int internalGetIndex() {
217       return IntegerDBID.this.id;
218     }
219 
220     @Override
valid()221     public boolean valid() {
222       return (pos == 0);
223     }
224 
225     @Override
hashCode()226     public int hashCode() {
227       // Override, because we also are overriding equals.
228       return super.hashCode();
229     }
230 
231     @Override
equals(Object other)232     public boolean equals(Object other) {
233       if(other instanceof DBID) {
234         LoggingUtil.warning("Programming error detected: DBIDItr.equals(DBID). Use sameDBID()!", new Throwable());
235       }
236       return super.equals(other);
237     }
238 
239     @Override
toString()240     public String toString() {
241       return Integer.toString(internalGetIndex());
242     }
243   }
244 
245   /**
246    * Dynamic sized serializer, using varint.
247    *
248    * @author Erich Schubert
249    */
250   public static class DynamicSerializer implements ByteBufferSerializer<DBID> {
251     /**
252      * Constructor. Protected: use static instance!
253      */
DynamicSerializer()254     public DynamicSerializer() {
255       super();
256     }
257 
258     @Override
fromByteBuffer(ByteBuffer buffer)259     public DBID fromByteBuffer(ByteBuffer buffer) throws IOException {
260       return new IntegerDBID(ByteArrayUtil.readSignedVarint(buffer));
261     }
262 
263     @Override
toByteBuffer(ByteBuffer buffer, DBID object)264     public void toByteBuffer(ByteBuffer buffer, DBID object) throws IOException {
265       ByteArrayUtil.writeSignedVarint(buffer, ((IntegerDBID) object).id);
266     }
267 
268     @Override
getByteSize(DBID object)269     public int getByteSize(DBID object) throws IOException {
270       return ByteArrayUtil.getSignedVarintSize(((IntegerDBID) object).id);
271     }
272   }
273 
274   /**
275    * Static sized serializer, using regular integers.
276    *
277    * @author Erich Schubert
278    */
279   public static class StaticSerializer implements FixedSizeByteBufferSerializer<DBID> {
280     /**
281      * Constructor. Protected: use static instance!
282      */
StaticSerializer()283     public StaticSerializer() {
284       super();
285     }
286 
287     @Override
fromByteBuffer(ByteBuffer buffer)288     public DBID fromByteBuffer(ByteBuffer buffer) throws IOException {
289       return new IntegerDBID(buffer.getInt());
290     }
291 
292     @Override
toByteBuffer(ByteBuffer buffer, DBID object)293     public void toByteBuffer(ByteBuffer buffer, DBID object) throws IOException {
294       buffer.putInt(((IntegerDBID) object).id);
295     }
296 
297     @Override
getByteSize(DBID object)298     public int getByteSize(DBID object) throws IOException {
299       return getFixedByteSize();
300     }
301 
302     @Override
getFixedByteSize()303     public int getFixedByteSize() {
304       return ByteArrayUtil.SIZE_INT;
305     }
306   }
307 
308   /**
309    * The public instance to use for dynamic serialization.
310    */
311   public static final ByteBufferSerializer<DBID> DYNAMIC_SERIALIZER = new DynamicSerializer();
312 
313   /**
314    * The public instance to use for static serialization.
315    */
316   public static final FixedSizeByteBufferSerializer<DBID> STATIC_SERIALIZER = new StaticSerializer();
317 }
318