*** empty log message ***
[IRC.git] / Robust / Transactions / mytuplesoup / src / com / solidosystems / tuplesoup / core / Value.java
1 /*
2  * Copyright (c) 2007, Solido Systems
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * Neither the name of Solido Systems nor the names of its contributors may be
16  * used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31  
32  package com.solidosystems.tuplesoup.core;
33  
34  import java.util.*;
35  import java.io.*;
36  
37  /**
38   * The Value class holds a single data value.
39   * Current size estimate without string data: 8+4+4+8+8 = 32 bytes pr value in mem
40   */
41  public class Value{
42      public final static int NULL=0;
43      public final static int STRING=1;
44      public final static int INT=2;
45      public final static int LONG=3;
46      public final static int FLOAT=4;
47      public final static int DOUBLE=5;
48      public final static int BOOLEAN=6;
49      public final static int TIMESTAMP=7;
50      public final static int BINARY=8;
51
52      private byte type=NULL; // The type of the value being held
53      private String str_value=null;
54      private long int_value=0;
55      private double float_value=0.0;
56      private byte[] binary=null;
57
58      /**
59       * Returns the numerical type id for this value.
60       */
61      public int getType(){
62          return type;
63      }
64      
65      /**
66       * Returns the name this value's type.
67       */
68      public String getTypeName(){
69          switch(type){
70               case STRING     : return "string";
71               case INT        : return "int";
72               case LONG       : return "long";
73               case FLOAT      : return "float";
74               case DOUBLE     : return "double";
75               case BOOLEAN    : return "boolean";
76               case TIMESTAMP  : return "timestamp";
77               case BINARY     : return "binary";
78           }
79           return "null";
80      }
81
82      /**
83       * An implementation of the hashCode method as defined in java.lang.Object
84       * 
85       * @return a hash code value for this object
86       */
87      public int hashCode(){
88          int hash=0;
89          switch(type){
90              case STRING     : hash+=str_value.hashCode();
91              case INT        : hash+=(int)int_value;
92              case LONG       : hash+=(int)int_value;
93              case FLOAT      : hash+=(int)float_value;
94              case DOUBLE     : hash+=(int)float_value;
95              case BOOLEAN    : hash+=(int)int_value;
96              case TIMESTAMP  : hash+=(int)int_value;
97              case BINARY     : hash+=binary.hashCode();
98          }
99          return hash;
100      }
101
102      /**
103       * Returns true only if this Value has specifically been set to null.
104       *
105       * @return true if the data being held is null, false otherwise
106       */
107      public boolean isNull(){
108          return type==NULL;
109      }
110
111      /**
112       * Returns -1, 0 or 1 if this value is smaller, equal or larger than the value given as a parameter.
113       */
114      public int compareTo(Value value){
115          if(type==STRING){
116              return str_value.compareTo(value.getString());
117          }
118          if(lessThan(value))return -1;
119          if(greaterThan(value))return 1;
120          return 0;
121      }
122
123      /**
124       * Attempts to compare this Value to the value given as parameter and returns true if this value is less than the value given as a parameter.
125       * The types used for the comparison will always be based on the type of this Value based on the following rules.
126       * <ul>
127       *   <li>If this Value is a numeric type, then the other Value will be asked to deliver the same numeric type for the comparison.
128       *   <li>If this Value is a string, then both values will be asked to deliver a double value for the comparison.
129       *   <li>If this Value is a timestamp, then both values will be asked to deliver a long value for the comparison.
130       *   <li>If this Value is a boolean, false will be returned.
131       * </ul>
132       *
133       * @param value the value this value should be compared to
134       * @return true if this value is less than the value given as a parameter, false otherwise
135       */
136      public boolean lessThan(Value value){
137          switch(type){
138              case STRING     : return getDouble()<value.getDouble();
139              case INT        : return getInt()<value.getInt();
140              case LONG       : return getLong()<value.getLong();
141              case FLOAT      : return getFloat()<value.getFloat();
142              case DOUBLE     : return getDouble()<value.getDouble();
143              case TIMESTAMP  : return getLong()<value.getLong();
144          }
145          return false;
146      }
147
148      /**
149       * Attempts to compare this Value to the value given as parameter and returns true if this value is greater than the value given as a parameter.
150       * The types used for the comparison will always be based on the type of this Value based on the following rules.
151       * <ul>
152       *   <li>If this Value is a numeric type, then the other Value will be asked to deliver the same numeric type for the comparison.
153       *   <li>If this Value is a string, then both values will be asked to deliver a double value for the comparison.
154       *   <li>If this Value is a timestamp, then both values will be asked to deliver a long value for the comparison.
155       *   <li>If this Value is a boolean, false will be returned.
156       * </ul>
157       *
158       * @param value the value this value should be compared to
159       * @return true if this value is greater than the value given as a parameter, false otherwise
160       */
161      public boolean greaterThan(Value value){
162          switch(type){
163              case STRING     : return getDouble()>value.getDouble();
164              case INT        : return getInt()>value.getInt();
165              case LONG       : return getLong()>value.getLong();
166              case FLOAT      : return getFloat()>value.getFloat();
167              case DOUBLE     : return getDouble()>value.getDouble();
168              case TIMESTAMP  : return getLong()>value.getLong();
169          }
170          return false;
171      }
172
173      /**
174       * Returns true if the string representation of this value starts with the string representation of the value given as parameter.
175       */
176      public boolean startsWith(Value value){
177          return getString().startsWith(value.getString());
178      }
179
180      /**
181       * Returns true if the string representation of this value ends with the string representation of the value given as parameter.
182       */
183      public boolean endsWith(Value value){
184          return getString().endsWith(value.getString());
185      }
186
187      /**
188       * Returns true if the string representation of this value contains the string representation of the value given as parameter.
189       */
190      public boolean contains(Value value){
191          return getString().indexOf(value.getString())>-1;
192      }
193
194      /**
195       * Returns true if the contents of this value equals the contents of the value given as parameter.
196       */
197      public boolean equals(Object obj){
198          try{
199              Value val=(Value)obj;
200              if(val.type==type){
201                  switch(type){
202                      case NULL       : return true;
203                      case STRING     : return str_value.equals(val.str_value);
204                      case INT        : return int_value==val.int_value;
205                      case LONG       : return int_value==val.int_value;
206                      case FLOAT      : return float_value==val.float_value;
207                      case DOUBLE     : return float_value==val.float_value;
208                      case BOOLEAN    : return int_value==val.int_value;
209                      case TIMESTAMP  : return int_value==val.int_value;
210                      case BINARY     : if(binary.length==val.binary.length){
211                                           for(int i=0;i<binary.length;i++){
212                                               if(binary[i]!=val.binary[i])return false;
213                                           }
214                                        }
215                                        return true;
216                  }
217              }
218          }catch(Exception e){}
219          return false;
220      }
221
222      /**
223       * Returns a string representation of this object.
224       */
225      public String toString(){
226          return getString();
227      }
228
229      /**
230       * Returns a string representation of this object (identical to toString)
231       */
232      public String get(){
233          return getString();
234      }
235
236      /**
237       * Returns this value as an xml tag with the given key set as an attribute called name.
238       * The following string is an example of the int value 1234 created with the key foo &lt;value name="foo" type="int"&gt;1234&lt;/value&gt;
239       */
240      public String toBasicXMLString(String key){
241          switch(type){
242              case STRING     : return "<value name=\""+key+"\" type=\"string\">"+str_value+"</value>";
243              case INT        : return "<value name=\""+key+"\"  type=\"int\">"+int_value+"</value>";
244              case LONG       : return "<value name=\""+key+"\"  type=\"long\">"+int_value+"</value>";
245              case FLOAT      : return "<value name=\""+key+"\"  type=\"float\">"+float_value+"</value>";
246              case DOUBLE     : return "<value name=\""+key+"\"  type=\"double\">"+float_value+"</value>";
247              case BOOLEAN    : if(int_value==1){
248                                      return "<value name=\""+key+"\"  type=\"boolean\">TRUE</value>";
249                                }else{
250                                      return "<value name=\""+key+"\"  type=\"boolean\">FALSE</value>";
251                                }
252              case TIMESTAMP  : return "<value name=\""+key+"\"  type=\"timestamp\">"+new Date(int_value).toString()+"</value>";
253              case BINARY     : return "<value name=\""+key+"\" type=\"binary\">"+getString()+"</value>";
254          }
255          return "<value name=\""+key+"\"  type=\"null\"></value>";
256      }
257      
258      /**
259        * Returns this value as an xml tag.
260        * The following string is an example of the int value 1234 &lt;value type="int"&gt;1234&lt;/value&gt;
261        */
262      public String toBasicXMLString(){
263           switch(type){
264               case STRING     : return "<value type=\"string\">"+str_value+"</value>";
265               case INT        : return "<value type=\"int\">"+int_value+"</value>";
266               case LONG       : return "<value type=\"long\">"+int_value+"</value>";
267               case FLOAT      : return "<value type=\"float\">"+float_value+"</value>";
268               case DOUBLE     : return "<value type=\"double\">"+float_value+"</value>";
269               case BOOLEAN    : if(int_value==1){
270                                       return "<value type=\"boolean\">TRUE</value>";
271                                 }else{
272                                       return "<value type=\"boolean\">FALSE</value>";
273                                 }
274               case TIMESTAMP  : return "<value type=\"timestamp\">"+new Date(int_value).toString()+"</value>";
275               case BINARY     : return "<value type=\"binary\">"+getString()+"</value>";
276           }
277           return "<value type=\"null\"></value>";
278       }
279
280      /**
281       * Returns a string representation of this value
282       */
283      public String getString(){
284          switch(type){
285              case STRING     : return str_value;
286              case INT        : return ""+int_value;
287              case LONG       : return ""+int_value;
288              case FLOAT      : return ""+float_value;
289              case DOUBLE     : return ""+float_value;
290              case BOOLEAN    : if(int_value==1){
291                                      return "TRUE";
292                                }else{
293                                      return "FALSE";
294                                }
295              case TIMESTAMP  : return new Date(int_value).toString();
296              case BINARY     : StringBuffer buf=new StringBuffer();
297                                for(int i=0;i<binary.length;i++){
298                                   byte b=binary[i];
299                                   buf.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1,3));
300                                }
301                                return buf.toString();
302          }
303          return "";
304      }
305
306      /**
307       * Attempts to return an integer representation of this value.
308       * Numerical values will be returned directly (demoted if necessary).
309       * Boolean values will be returned as 1 or 0.
310       * Timestamp values will be returned as a unix timestamp representation divided by 1000.
311       * String values will be atempted to be converted into an int, if that fails, 0 will be returned.
312       * Everything else will return 0.
313       */
314      public int getInt(){
315          try{
316              switch(type){
317                  case STRING     : return Integer.parseInt(str_value);
318                  case INT        : return (int)int_value;
319                  case LONG       : return (int)int_value;
320                  case FLOAT      : return (int)float_value;
321                  case DOUBLE     : return (int)float_value;
322                  case BOOLEAN    : return (int)int_value;
323                  case TIMESTAMP  : return (int)(int_value/1000);
324              }
325          }catch(Exception e){}
326          return 0;
327      }
328
329      /**
330        * Attempts to return a long representation of this value.
331        * Numerical values will be returned directly.
332        * Boolean values will be returned as 1 or 0.
333        * Timestamp values will be returned as a unix timestamp representation.
334        * String values will be atempted to be converted into an int, if that fails, 0 will be returned.
335        * Everything else will return 0.
336        */
337      public long getLong(){
338          try{
339              switch(type){
340                  case STRING     : return Long.parseLong(str_value);
341                  case INT        : return int_value;
342                  case LONG       : return int_value;
343                  case FLOAT      : return (long)float_value;
344                  case DOUBLE     : return (long)float_value;
345                  case BOOLEAN    : return int_value;
346                  case TIMESTAMP  : return int_value;
347              }
348          }catch(Exception e){}
349          return 0;
350      }
351
352      /**
353       * Attempts to return a float representation of this value.
354       * Numerical values will be returned directly (demoted if necessary).
355       * Boolean values will be returned as 1 or 0.
356       * Timestamp values will be returned as a unix timestamp representation divided by 1000.
357       * String values will be atempted to be converted into a float, if that fails, 0 will be returned.
358       * Everything else will return 0.
359       */
360      public float getFloat(){
361          try{
362              switch(type){
363                  case STRING     : return Float.parseFloat(str_value);
364                  case INT        : return (float)int_value;
365                  case LONG       : return (float)int_value;
366                  case FLOAT      : return (float)float_value;
367                  case DOUBLE     : return (float)float_value;
368                  case BOOLEAN    : return (float)int_value;
369                  case TIMESTAMP  : return (float)(int_value/1000);
370              }
371          }catch(Exception e){}
372          return 0.0f;
373      }
374
375      /**
376       * Attempts to return a double representation of this value.
377       * Numerical values will be returned directly.
378       * Boolean values will be returned as 1 or 0.
379       * Timestamp values will be returned as a unix timestamp representation divided by 1000.
380       * String values will be atempted to be converted into a float, if that fails, 0 will be returned.
381       * Everything else will return 0.
382       */
383      public double getDouble(){
384          try{
385              switch(type){
386                  case STRING     : return Double.parseDouble(str_value);
387                  case INT        : return (double)int_value;
388                  case LONG       : return (double)int_value;
389                  case FLOAT      : return (double)float_value;
390                  case DOUBLE     : return (double)float_value;
391                  case BOOLEAN    : return (double)int_value;
392                  case TIMESTAMP  : return (double)(int_value);
393              }
394          }catch(Exception e){}
395          return 0.0;
396      }
397
398      /**
399       * Returns a boolean representation of this value.
400       * Boolean values will be returned directly.
401       * Integer values equalling 1 will be returned as true.
402       * String values equalling true,1,t,yes,on will be returned as true (case insensitive).
403       * Everything else will be returned as false.
404       *
405       * @return a boolean representation of this value.
406       */
407      public boolean getBoolean(){
408          try{
409              switch(type){
410                  case STRING     : if(str_value.toLowerCase().trim().equals("true"))return true;
411                                    if(str_value.trim().equals("1"))return true;
412                                    if(str_value.toLowerCase().trim().equals("t"))return true;
413                                    if(str_value.toLowerCase().trim().equals("yes"))return true;
414                                    if(str_value.toLowerCase().trim().equals("on"))return true;
415                  case INT        : if(int_value==1)return true;
416                  case LONG       : if(int_value==1)return true;
417                  case FLOAT      : if(float_value==1.0f)return true;
418                  case DOUBLE     : if(float_value==1.0)return true;
419                  case BOOLEAN    : if(int_value==1)return true;
420              }
421          }catch(Exception e){}
422          return false;
423      }
424
425      /**
426       * Attempts to return this value as a Date object.
427       * For non date numerical values, the following rules will be used for conversion:
428       * int and float will be multiplied by 1000 and used as a unix timestamp.
429       * long and double will be used directly as a unix timestamp.
430       * Any other type will result in a Date object initialized with 0.
431       *
432       * @return a Date object representation of this value
433       */
434      public Date getTimestamp(){
435          try{
436              switch(type){
437                  case INT        : return new Date(int_value*1000l);
438                  case LONG       : return new Date(int_value);
439                  case FLOAT      : return new Date((long)(float_value*1000l));
440                  case DOUBLE     : return new Date((long)float_value);
441                  case TIMESTAMP  : return new Date(int_value);
442              }
443          }catch(Exception e){}
444          return new Date(0);
445      }
446
447      public byte[] getBinary(){
448          switch(type){
449              case BINARY        : return binary;
450          }
451          return null;
452      }
453
454      /**
455       * Attempts to write this Value to the given DataOutputStream.
456       * The Value will be written as a byte signifying the type of the Value, followed by the actual Value in the native format used by DataOutput.
457       * The exception to this is the string which is written as an integer followed by each character as a single char.
458       * 
459       * @param out the DataOutputStream the Value should be written to
460       */
461      public void writeToStream(DataOutputStream out) throws IOException{
462          out.writeByte(type);
463          switch(type){
464              case STRING :   out.writeInt(str_value.length());
465                              for(int i=0;i<str_value.length();i++){
466                                  out.writeChar(str_value.charAt(i));
467                              }
468                          break;
469              case INT    :   out.writeInt((int)int_value);
470                          break;
471              case LONG   :   out.writeLong(int_value);
472                          break;
473              case FLOAT  :   out.writeFloat((float)float_value);
474                          break;
475              case DOUBLE :   out.writeDouble(float_value);
476                          break;
477              case BOOLEAN:   out.writeBoolean(int_value==1);
478                          break;
479              case TIMESTAMP: out.writeLong(int_value);
480                          break;
481              case BINARY : out.writeInt(binary.length);
482                            out.write(binary,0,binary.length);
483                          break;
484          }
485      }
486      
487      /**
488        * Attempts to write this Value to the given DataOutputStream.
489        * The Value will be written as a byte signifying the type of the Value, followed by the actual Value in the native format used by DataOutput.
490        * The exception to this is the string which is written as an integer followed by each character as a single char.
491        * 
492        * @param out the DataOutputStream the Value should be written to
493        */
494       public void writeToFile(DataOutput out) throws IOException{
495           out.writeByte(type);
496           switch(type){
497               case STRING :   out.writeInt(str_value.length());
498                               for(int i=0;i<str_value.length();i++){
499                                   out.writeChar(str_value.charAt(i));
500                               }
501                           break;
502               case INT    :   out.writeInt((int)int_value);
503                           break;
504               case LONG   :   out.writeLong(int_value);
505                           break;
506               case FLOAT  :   out.writeFloat((float)float_value);
507                           break;
508               case DOUBLE :   out.writeDouble(float_value);
509                           break;
510               case BOOLEAN:   out.writeBoolean(int_value==1);
511                           break;
512               case TIMESTAMP: out.writeLong(int_value);
513                           break;
514               case BINARY : out.writeInt(binary.length);
515                             out.write(binary,0,binary.length);
516           }
517       }
518
519      /**
520       * Attempts to read a new Value from the given DataInputStream.
521       * The Value should be in the format delivered by writeToStream.
522       *
523       * @param in the DataInputStream the Value should be read from
524       * @return the Value read from the stream
525       */
526      public static Value readFromStream(DataInputStream in) throws IOException{
527          byte type=in.readByte();
528          switch(type){
529              case STRING :   int size=in.readInt();
530                              StringBuffer buf=new StringBuffer();
531                              for(int i=0;i<size;i++){
532                                  buf.append(in.readChar());
533                              }
534                              return new Value(buf.toString());
535              case INT    :   return new Value(in.readInt());
536              case LONG   :   return new Value(in.readLong());
537              case FLOAT  :   return new Value(in.readFloat());
538              case DOUBLE :   return new Value(in.readDouble());
539              case BOOLEAN:   return new Value(in.readBoolean());
540              case TIMESTAMP: return new Value(new Date(in.readLong()));
541              case BINARY : int length=in.readInt();
542                            byte[] abuf=new byte[length];
543                            int read=0;
544                            while(read<length){
545                                read+=in.read(abuf,read,length-read);
546                            }
547                            return new Value(abuf);
548
549          }
550          return new Value();
551      }
552
553     /**
554      * Initializes this Value with the given String.
555      * 
556      * @param val the value this Value object should represent
557      */
558      public Value(String val){
559          str_value=val;
560          type=STRING;
561      }
562      
563      public Value(byte[] val){
564           binary=val;
565           type=BINARY;
566       }
567
568      /**
569       * Initializes this Value with the given Date.
570       * The Dates internal long value delivered by the getTime() method will be used.
571       * 
572       * @param val the value this Value object should represent
573       */
574      public Value(Date val){
575          int_value=val.getTime();
576          type=TIMESTAMP;
577      }
578
579      /**
580       * Initializes this Value as null.
581       */
582      public Value(){
583          type=NULL;
584      }
585
586      /**
587       * Initializes this Value with the given int.
588       * 
589       * @param val the value this Value object should represent
590       */
591      public Value(int val){
592          int_value=val;
593          type=INT;
594      }
595
596      /**
597       * Initializes this Value with the given long.
598       * 
599       * @param val the value this Value object should represent
600       */
601      public Value(long val){
602          int_value=val;
603          type=LONG;
604      }
605
606      /**
607       * Initializes this Value with the given float.
608       * 
609       * @param val the value this Value object should represent
610       */
611      public Value(float val){
612          float_value=val;
613          type=FLOAT;
614      }
615
616      /**
617       * Initializes this Value with the given double.
618       * 
619       * @param val the value this Value object should represent
620       */
621      public Value(double val){
622          float_value=val;
623          type=DOUBLE;
624      }
625
626      /**
627       * Initializes this Value with the given boolean.
628       * 
629       * @param val the value this Value object should represent
630       */
631      public Value(boolean val){
632          if(val){
633              int_value=1;
634          }else{
635              int_value=0;
636          }
637          type=BOOLEAN;
638      }  
639  }