001    /*
002     * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003     * Copyright (C) 2011 NightLabs Consulting GmbH
004     *
005     * This program is free software: you can redistribute it and/or modify
006     * it under the terms of the GNU Affero General Public License as
007     * published by the Free Software Foundation, either version 3 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU Affero General Public License for more details.
014     *
015     * You should have received a copy of the GNU Affero General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    package org.cumulus4j.store.model;
019    
020    import java.util.Collections;
021    import java.util.HashSet;
022    import java.util.Set;
023    
024    /**
025     * Helper for en- &amp; decoding the decrypted (plain) contents of
026     * {@link IndexEntry#getIndexValue() IndexEntry.indexValue}. This byte-array holds
027     * references to {@link DataEntry#getDataEntryID() DataEntry.dataEntryID}s.
028     *
029     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
030     */
031    public class IndexValue
032    {
033            private Set<Long> dataEntryIDs = new HashSet<Long>(); // A HashSet is faster than a TreeSet and I don't see a need for the sorting.
034    
035            /**
036             * Create an empty instance of <code>IndexValue</code>. This is equivalent to
037             * calling {@link #IndexValue(byte[])} with a <code>null</code> or an empty argument.
038             */
039            public IndexValue() {
040                    this(null);
041            }
042    
043            /**
044             * Create an <code>IndexValue</code> instance from the decrypted (plain) byte-array
045             * which is stored in {@link IndexEntry#getIndexValue() IndexEntry.indexValue}.
046             *
047             * @param indexValueByteArray the plain (decrypted) byte-array of {@link IndexEntry#getIndexValue()} or <code>null</code>
048             * (<code>null</code> is equivalent to an empty byte-array). This byte-array is what is created by {@link #toByteArray()}.
049             */
050            public IndexValue(byte[] indexValueByteArray) {
051                    if (indexValueByteArray != null) {
052                            if ((indexValueByteArray.length % 8) != 0)
053                                    throw new IllegalArgumentException("indexValueByteArray.length is not dividable by 8!");
054    
055                            for (int i = 0; i < indexValueByteArray.length / 8; ++i) {
056                                    long dataEntryID =
057                                            (((long)indexValueByteArray[i * 8 + 0] & 0xff) << 56) +
058                                            (((long)indexValueByteArray[i * 8 + 1] & 0xff) << 48) +
059                                            (((long)indexValueByteArray[i * 8 + 2] & 0xff) << 40) +
060                                            (((long)indexValueByteArray[i * 8 + 3] & 0xff) << 32) +
061                                            (((long)indexValueByteArray[i * 8 + 4] & 0xff) << 24) +
062                                            (((long)indexValueByteArray[i * 8 + 5] & 0xff) << 16) +
063                                            (((long)indexValueByteArray[i * 8 + 6] & 0xff) <<  8) +
064                                            (indexValueByteArray[i * 8 + 7] & 0xff)
065                                    ;
066                                    dataEntryIDs.add(dataEntryID);
067                            }
068                    }
069            }
070    
071            /**
072             * Get a byte-array with all {@link #getDataEntryIDs() dataEntryIDs}. It can be passed to
073             * {@link #IndexValue(byte[])} later (e.g. after encrypting, persisting, loading &amp; decrypting).
074             * @return a byte-array holding all dataEntryIDs managed by this instance.
075             */
076            public byte[] toByteArray()
077            {
078                    byte[] result = new byte[dataEntryIDs.size() * 8];
079                    int i = -1;
080                    for (Long dataEntryID : dataEntryIDs) {
081                            long v = dataEntryID;
082                            result[++i] = (byte)(v >>> 56);
083                            result[++i] = (byte)(v >>> 48);
084                            result[++i] = (byte)(v >>> 40);
085                            result[++i] = (byte)(v >>> 32);
086                            result[++i] = (byte)(v >>> 24);
087                            result[++i] = (byte)(v >>> 16);
088                            result[++i] = (byte)(v >>> 8);
089                            result[++i] = (byte)v;
090                    }
091                    return result;
092            }
093    
094            /**
095             * Get {@link DataEntry#getDataEntryID() dataEntryID}s referencing those {@link DataEntry}s which this <code>IndexValue</code>
096             * (or more precisely the {@link IndexEntry} from which this <code>IndexValue</code> was created) points to.
097             * @return the object-IDs of the <code>DataEntry</code> instances that are referenced by this index entry.
098             */
099            public Set<Long> getDataEntryIDs() {
100                    return Collections.unmodifiableSet(dataEntryIDs);
101            }
102    
103            public boolean isDataEntryIDsEmpty()
104            {
105                    return dataEntryIDs.isEmpty();
106            }
107    
108            public boolean addDataEntryID(long dataEntryID)
109            {
110                    return dataEntryIDs.add(dataEntryID);
111            }
112    
113            public boolean removeDataEntryID(long dataEntryID)
114            {
115                    return dataEntryIDs.remove(dataEntryID);
116            }
117    
118            @Override
119            public int hashCode() {
120                    return dataEntryIDs.hashCode();
121            }
122    
123            @Override
124            public boolean equals(Object obj) {
125                    if (this == obj) return true;
126                    if (obj == null) return false;
127                    if (getClass() != obj.getClass()) return false;
128                    IndexValue other = (IndexValue) obj;
129                    return this.dataEntryIDs.equals(other.dataEntryIDs);
130            }
131    
132    //      public static void main(String[] args) {
133    //              Random random = new Random();
134    //              IndexValue indexValue1 = new IndexValue();
135    //              for (int i = 0; i < 100; ++i) {
136    //                      long dataEntryID = random.nextLong();
137    //                      indexValue1.addDataEntryID(dataEntryID);
138    //              }
139    //
140    //              for (Long dataEntryID : indexValue1.getDataEntryIDs()) {
141    //                      System.out.println(dataEntryID);
142    //              }
143    //
144    //              System.out.println();
145    //              System.out.println();
146    //              System.out.println();
147    //
148    //              byte[] byteArray = indexValue1.toByteArray();
149    //
150    //              IndexValue indexValue2 = new IndexValue(byteArray);
151    //              for (Long dataEntryID : indexValue2.getDataEntryIDs()) {
152    //                      System.out.println(dataEntryID);
153    //              }
154    //
155    //              System.out.println();
156    //              System.out.println();
157    //              System.out.println();
158    //
159    //              System.out.println(indexValue1.equals(indexValue2));
160    //      }
161    
162    }