View Javadoc

1   /**
2    * Copyright 2007 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase;
21  
22  import java.io.DataInput;
23  import java.io.DataOutput;
24  import java.io.IOException;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Map;
28  
29  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
30  import org.apache.hadoop.hbase.io.hfile.Compression;
31  import org.apache.hadoop.hbase.io.hfile.HFile;
32  import org.apache.hadoop.hbase.regionserver.StoreFile;
33  import org.apache.hadoop.hbase.regionserver.StoreFile.BloomType;
34  import org.apache.hadoop.hbase.util.Bytes;
35  import org.apache.hadoop.io.Text;
36  import org.apache.hadoop.io.WritableComparable;
37  
38  /**
39   * An HColumnDescriptor contains information about a column family such as the
40   * number of versions, compression settings, etc.
41   *
42   * It is used as input when creating a table or adding a column. Once set, the
43   * parameters that specify a column cannot be changed without deleting the
44   * column and recreating it. If there is data stored in the column, it will be
45   * deleted when the column is deleted.
46   */
47  public class HColumnDescriptor implements WritableComparable<HColumnDescriptor> {
48    // For future backward compatibility
49  
50    // Version 3 was when column names become byte arrays and when we picked up
51    // Time-to-live feature.  Version 4 was when we moved to byte arrays, HBASE-82.
52    // Version 5 was when bloom filter descriptors were removed.
53    // Version 6 adds metadata as a map where keys and values are byte[].
54    // Version 7 -- add new compression and hfile blocksize to HColumnDescriptor (HBASE-1217)
55    // Version 8 -- reintroduction of bloom filters, changed from boolean to enum
56    private static final byte COLUMN_DESCRIPTOR_VERSION = (byte)8;
57  
58    public static final String COMPRESSION = "COMPRESSION";
59    public static final String COMPRESSION_COMPACT = "COMPRESSION_COMPACT";
60    public static final String BLOCKCACHE = "BLOCKCACHE";
61    
62    /**
63     * Size of storefile/hfile 'blocks'.  Default is {@link #DEFAULT_BLOCKSIZE}.
64     * Use smaller block sizes for faster random-access at expense of larger
65     * indices (more memory consumption).
66     */
67    public static final String BLOCKSIZE = "BLOCKSIZE";
68  
69    public static final String LENGTH = "LENGTH";
70    public static final String TTL = "TTL";
71    public static final String BLOOMFILTER = "BLOOMFILTER";
72    public static final String FOREVER = "FOREVER";
73    public static final String REPLICATION_SCOPE = "REPLICATION_SCOPE";
74    public static final String MIN_VERSIONS = "MIN_VERSIONS";
75  
76    /**
77     * Default compression type.
78     */
79    public static final String DEFAULT_COMPRESSION =
80      Compression.Algorithm.NONE.getName();
81  
82    /**
83     * Default number of versions of a record to keep.
84     */
85    public static final int DEFAULT_VERSIONS = 3;
86  
87    /**
88     * Default is not to keep a minimum of versions.
89     */
90    public static final int DEFAULT_MIN_VERSIONS = 0;
91  
92    /*
93     * Cache here the HCD value.
94     * Question: its OK to cache since when we're reenable, we create a new HCD?
95     */
96    private volatile Integer blocksize = null;
97  
98    /**
99     * Default setting for whether to serve from memory or not.
100    */
101   public static final boolean DEFAULT_IN_MEMORY = false;
102 
103   /**
104    * Default setting for whether to use a block cache or not.
105    */
106   public static final boolean DEFAULT_BLOCKCACHE = true;
107 
108   /**
109    * Default size of blocks in files stored to the filesytem (hfiles).
110    */
111   public static final int DEFAULT_BLOCKSIZE = HFile.DEFAULT_BLOCKSIZE;
112 
113   /**
114    * Default setting for whether or not to use bloomfilters.
115    */
116   public static final String DEFAULT_BLOOMFILTER = StoreFile.BloomType.NONE.toString();
117 
118   /**
119    * Default time to live of cell contents.
120    */
121   public static final int DEFAULT_TTL = HConstants.FOREVER;
122 
123   /**
124    * Default scope.
125    */
126   public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
127 
128   private final static Map<String, String> DEFAULT_VALUES = new HashMap<String, String>();
129   static {
130       DEFAULT_VALUES.put(BLOOMFILTER, DEFAULT_BLOOMFILTER);
131       DEFAULT_VALUES.put(REPLICATION_SCOPE, String.valueOf(DEFAULT_REPLICATION_SCOPE));
132       DEFAULT_VALUES.put(HConstants.VERSIONS, String.valueOf(DEFAULT_VERSIONS));
133       DEFAULT_VALUES.put(COMPRESSION, DEFAULT_COMPRESSION);
134       DEFAULT_VALUES.put(TTL, String.valueOf(DEFAULT_TTL));
135       DEFAULT_VALUES.put(BLOCKSIZE, String.valueOf(DEFAULT_BLOCKSIZE));
136       DEFAULT_VALUES.put(HConstants.IN_MEMORY, String.valueOf(DEFAULT_IN_MEMORY));
137       DEFAULT_VALUES.put(BLOCKCACHE, String.valueOf(DEFAULT_BLOCKCACHE));
138   }
139 
140   // Column family name
141   private byte [] name;
142 
143   // Column metadata
144   protected Map<ImmutableBytesWritable,ImmutableBytesWritable> values =
145     new HashMap<ImmutableBytesWritable,ImmutableBytesWritable>();
146 
147   /*
148    * Cache the max versions rather than calculate it every time.
149    */
150   private int cachedMaxVersions = -1;
151 
152   /**
153    * Default constructor. Must be present for Writable.
154    */
155   public HColumnDescriptor() {
156     this.name = null;
157   }
158 
159   /**
160    * Construct a column descriptor specifying only the family name
161    * The other attributes are defaulted.
162    *
163    * @param familyName Column family name. Must be 'printable' -- digit or
164    * letter -- and may not contain a <code>:<code>
165    */
166   public HColumnDescriptor(final String familyName) {
167     this(Bytes.toBytes(familyName));
168   }
169 
170   /**
171    * Construct a column descriptor specifying only the family name
172    * The other attributes are defaulted.
173    *
174    * @param familyName Column family name. Must be 'printable' -- digit or
175    * letter -- and may not contain a <code>:<code>
176    */
177   public HColumnDescriptor(final byte [] familyName) {
178     this (familyName == null || familyName.length <= 0?
179       HConstants.EMPTY_BYTE_ARRAY: familyName, DEFAULT_VERSIONS,
180       DEFAULT_COMPRESSION, DEFAULT_IN_MEMORY, DEFAULT_BLOCKCACHE,
181       DEFAULT_TTL, DEFAULT_BLOOMFILTER);
182   }
183 
184   /**
185    * Constructor.
186    * Makes a deep copy of the supplied descriptor.
187    * Can make a modifiable descriptor from an UnmodifyableHColumnDescriptor.
188    * @param desc The descriptor.
189    */
190   public HColumnDescriptor(HColumnDescriptor desc) {
191     super();
192     this.name = desc.name.clone();
193     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
194         desc.values.entrySet()) {
195       this.values.put(e.getKey(), e.getValue());
196     }
197     setMaxVersions(desc.getMaxVersions());
198   }
199 
200   /**
201    * Constructor
202    * @param familyName Column family name. Must be 'printable' -- digit or
203    * letter -- and may not contain a <code>:<code>
204    * @param maxVersions Maximum number of versions to keep
205    * @param compression Compression type
206    * @param inMemory If true, column data should be kept in an HRegionServer's
207    * cache
208    * @param blockCacheEnabled If true, MapFile blocks should be cached
209    * @param timeToLive Time-to-live of cell contents, in seconds
210    * (use HConstants.FOREVER for unlimited TTL)
211    * @param bloomFilter Bloom filter type for this column
212    *
213    * @throws IllegalArgumentException if passed a family name that is made of
214    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
215    * a <code>:</code>
216    * @throws IllegalArgumentException if the number of versions is &lt;= 0
217    */
218   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
219       final String compression, final boolean inMemory,
220       final boolean blockCacheEnabled,
221       final int timeToLive, final String bloomFilter) {
222     this(familyName, maxVersions, compression, inMemory, blockCacheEnabled,
223       DEFAULT_BLOCKSIZE, timeToLive, bloomFilter, DEFAULT_REPLICATION_SCOPE);
224   }
225 
226   /**
227    * Constructor
228    * @param familyName Column family name. Must be 'printable' -- digit or
229    * letter -- and may not contain a <code>:<code>
230    * @param maxVersions Maximum number of versions to keep
231    * @param compression Compression type
232    * @param inMemory If true, column data should be kept in an HRegionServer's
233    * cache
234    * @param blockCacheEnabled If true, MapFile blocks should be cached
235    * @param blocksize Block size to use when writing out storefiles.  Use
236    * smaller blocksizes for faster random-access at expense of larger indices
237    * (more memory consumption).  Default is usually 64k.
238    * @param timeToLive Time-to-live of cell contents, in seconds
239    * (use HConstants.FOREVER for unlimited TTL)
240    * @param bloomFilter Bloom filter type for this column
241    * @param scope The scope tag for this column
242    *
243    * @throws IllegalArgumentException if passed a family name that is made of
244    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
245    * a <code>:</code>
246    * @throws IllegalArgumentException if the number of versions is &lt;= 0
247    */
248   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
249       final String compression, final boolean inMemory,
250       final boolean blockCacheEnabled, final int blocksize,
251       final int timeToLive, final String bloomFilter, final int scope) {
252     this(familyName, DEFAULT_MIN_VERSIONS, maxVersions, compression, inMemory,
253         blockCacheEnabled, blocksize, timeToLive, bloomFilter, scope);
254   }
255 
256   /**
257    * Constructor
258    * @param familyName Column family name. Must be 'printable' -- digit or
259    * letter -- and may not contain a <code>:<code>
260    * @param minVersions Minimum number of versions to keep
261    * @param maxVersions Maximum number of versions to keep
262    * @param compression Compression type
263    * @param inMemory If true, column data should be kept in an HRegionServer's
264    * cache
265    * @param blockCacheEnabled If true, MapFile blocks should be cached
266    * @param blocksize Block size to use when writing out storefiles.  Use
267    * smaller blocksizes for faster random-access at expense of larger indices
268    * (more memory consumption).  Default is usually 64k.
269    * @param timeToLive Time-to-live of cell contents, in seconds
270    * (use HConstants.FOREVER for unlimited TTL)
271    * @param bloomFilter Bloom filter type for this column
272    * @param scope The scope tag for this column
273    *
274    * @throws IllegalArgumentException if passed a family name that is made of
275    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
276    * a <code>:</code>
277    * @throws IllegalArgumentException if the number of versions is &lt;= 0
278    */
279   public HColumnDescriptor(final byte [] familyName, final int minVersions,
280       final int maxVersions, final String compression, final boolean inMemory,
281       final boolean blockCacheEnabled, final int blocksize,
282       final int timeToLive, final String bloomFilter, final int scope) {
283     isLegalFamilyName(familyName);
284     this.name = familyName;
285 
286     if (maxVersions <= 0) {
287       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
288       // Until there is support, consider 0 or < 0 -- a configuration error.
289       throw new IllegalArgumentException("Maximum versions must be positive");
290     }
291 
292     if (minVersions > 0) {
293       if (timeToLive == HConstants.FOREVER) {
294         throw new IllegalArgumentException("Minimum versions requires TTL.");
295       }
296       if (minVersions > maxVersions) {
297         throw new IllegalArgumentException("Minimum versions must be <= "+
298             "maximum versions.");
299       }
300     }
301 
302     setMaxVersions(maxVersions);
303     setMinVersions(minVersions);
304     setInMemory(inMemory);
305     setBlockCacheEnabled(blockCacheEnabled);
306     setTimeToLive(timeToLive);
307     setCompressionType(Compression.Algorithm.
308       valueOf(compression.toUpperCase()));
309     setBloomFilterType(StoreFile.BloomType.
310       valueOf(bloomFilter.toUpperCase()));
311     setBlocksize(blocksize);
312     setScope(scope);
313   }
314 
315   /**
316    * @param b Family name.
317    * @return <code>b</code>
318    * @throws IllegalArgumentException If not null and not a legitimate family
319    * name: i.e. 'printable' and ends in a ':' (Null passes are allowed because
320    * <code>b</code> can be null when deserializing).  Cannot start with a '.'
321    * either.
322    */
323   public static byte [] isLegalFamilyName(final byte [] b) {
324     if (b == null) {
325       return b;
326     }
327     if (b[0] == '.') {
328       throw new IllegalArgumentException("Family names cannot start with a " +
329         "period: " + Bytes.toString(b));
330     }
331     for (int i = 0; i < b.length; i++) {
332       if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
333         throw new IllegalArgumentException("Illegal character <" + b[i] +
334           ">. Family names cannot contain control characters or colons: " +
335           Bytes.toString(b));
336       }
337     }
338     return b;
339   }
340 
341   /**
342    * @return Name of this column family
343    */
344   public byte [] getName() {
345     return name;
346   }
347 
348   /**
349    * @return Name of this column family
350    */
351   public String getNameAsString() {
352     return Bytes.toString(this.name);
353   }
354 
355   /**
356    * @param key The key.
357    * @return The value.
358    */
359   public byte[] getValue(byte[] key) {
360     ImmutableBytesWritable ibw = values.get(new ImmutableBytesWritable(key));
361     if (ibw == null)
362       return null;
363     return ibw.get();
364   }
365 
366   /**
367    * @param key The key.
368    * @return The value as a string.
369    */
370   public String getValue(String key) {
371     byte[] value = getValue(Bytes.toBytes(key));
372     if (value == null)
373       return null;
374     return Bytes.toString(value);
375   }
376 
377   /**
378    * @return All values.
379    */
380   public Map<ImmutableBytesWritable,ImmutableBytesWritable> getValues() {
381     return Collections.unmodifiableMap(values);
382   }
383 
384   /**
385    * @param key The key.
386    * @param value The value.
387    */
388   public void setValue(byte[] key, byte[] value) {
389     values.put(new ImmutableBytesWritable(key),
390       new ImmutableBytesWritable(value));
391   }
392 
393   /**
394    * @param key Key whose key and value we're to remove from HCD parameters.
395    */
396   public void remove(final byte [] key) {
397     values.remove(new ImmutableBytesWritable(key));
398   }
399 
400   /**
401    * @param key The key.
402    * @param value The value.
403    */
404   public void setValue(String key, String value) {
405     setValue(Bytes.toBytes(key), Bytes.toBytes(value));
406   }
407 
408   /** @return compression type being used for the column family */
409   public Compression.Algorithm getCompression() {
410     String n = getValue(COMPRESSION);
411     if (n == null) {
412       return Compression.Algorithm.NONE;
413     }
414     return Compression.Algorithm.valueOf(n.toUpperCase());
415   }
416 
417   /** @return compression type being used for the column family for major 
418       compression */
419   public Compression.Algorithm getCompactionCompression() {
420     String n = getValue(COMPRESSION_COMPACT);
421     if (n == null) {
422       return getCompression();
423     }
424     return Compression.Algorithm.valueOf(n.toUpperCase());
425   }
426 
427   /** @return maximum number of versions */
428   public int getMaxVersions() {
429     return this.cachedMaxVersions;
430   }
431 
432   /**
433    * @param maxVersions maximum number of versions
434    */
435   public void setMaxVersions(int maxVersions) {
436     setValue(HConstants.VERSIONS, Integer.toString(maxVersions));
437     cachedMaxVersions = maxVersions;
438   }
439 
440   /**
441    * @return The storefile/hfile blocksize for this column family.
442    */
443   public synchronized int getBlocksize() {
444     if (this.blocksize == null) {
445       String value = getValue(BLOCKSIZE);
446       this.blocksize = (value != null)?
447         Integer.decode(value): Integer.valueOf(DEFAULT_BLOCKSIZE);
448     }
449     return this.blocksize.intValue();
450   }
451 
452   /**
453    * @param s Blocksize to use when writing out storefiles/hfiles on this
454    * column family.
455    */
456   public void setBlocksize(int s) {
457     setValue(BLOCKSIZE, Integer.toString(s));
458     this.blocksize = null;
459   }
460 
461   /**
462    * @return Compression type setting.
463    */
464   public Compression.Algorithm getCompressionType() {
465     return getCompression();
466   }
467 
468   /**
469    * Compression types supported in hbase.
470    * LZO is not bundled as part of the hbase distribution.
471    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
472    * for how to enable it.
473    * @param type Compression type setting.
474    */
475   public void setCompressionType(Compression.Algorithm type) {
476     String compressionType;
477     switch (type) {
478       case LZO: compressionType = "LZO"; break;
479       case GZ: compressionType = "GZ"; break;
480       case SNAPPY: compressionType = "SNAPPY"; break;
481       default: compressionType = "NONE"; break;
482     }
483     setValue(COMPRESSION, compressionType);
484   }
485 
486   /**
487    * @return Compression type setting.
488    */
489   public Compression.Algorithm getCompactionCompressionType() {
490     return getCompactionCompression();
491   }
492 
493   /**
494    * Compression types supported in hbase.
495    * LZO is not bundled as part of the hbase distribution.
496    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
497    * for how to enable it.
498    * @param type Compression type setting.
499    */
500   public void setCompactionCompressionType(Compression.Algorithm type) {
501     String compressionType;
502     switch (type) {
503       case LZO: compressionType = "LZO"; break;
504       case GZ: compressionType = "GZ"; break;
505       case SNAPPY: compressionType = "SNAPPY"; break;
506       default: compressionType = "NONE"; break;
507     }
508     setValue(COMPRESSION_COMPACT, compressionType);
509   }
510 
511   /**
512    * @return True if we are to keep all in use HRegionServer cache.
513    */
514   public boolean isInMemory() {
515     String value = getValue(HConstants.IN_MEMORY);
516     if (value != null)
517       return Boolean.valueOf(value).booleanValue();
518     return DEFAULT_IN_MEMORY;
519   }
520 
521   /**
522    * @param inMemory True if we are to keep all values in the HRegionServer
523    * cache
524    */
525   public void setInMemory(boolean inMemory) {
526     setValue(HConstants.IN_MEMORY, Boolean.toString(inMemory));
527   }
528 
529   /**
530    * @return Time-to-live of cell contents, in seconds.
531    */
532   public int getTimeToLive() {
533     String value = getValue(TTL);
534     return (value != null)? Integer.valueOf(value).intValue(): DEFAULT_TTL;
535   }
536 
537   /**
538    * @param timeToLive Time-to-live of cell contents, in seconds.
539    */
540   public void setTimeToLive(int timeToLive) {
541     setValue(TTL, Integer.toString(timeToLive));
542   }
543 
544   /**
545    * @return The minimum number of versions to keep.
546    */
547   public int getMinVersions() {
548     String value = getValue(MIN_VERSIONS);
549     return (value != null)? Integer.valueOf(value).intValue(): 0;
550   }
551 
552   /**
553    * @param minVersions The minimum number of versions to keep.
554    * (used when timeToLive is set)
555    */
556   public void setMinVersions(int minVersions) {
557     setValue(MIN_VERSIONS, Integer.toString(minVersions));
558   }
559 
560   /**
561    * @return True if MapFile blocks should be cached.
562    */
563   public boolean isBlockCacheEnabled() {
564     String value = getValue(BLOCKCACHE);
565     if (value != null)
566       return Boolean.valueOf(value).booleanValue();
567     return DEFAULT_BLOCKCACHE;
568   }
569 
570   /**
571    * @param blockCacheEnabled True if MapFile blocks should be cached.
572    */
573   public void setBlockCacheEnabled(boolean blockCacheEnabled) {
574     setValue(BLOCKCACHE, Boolean.toString(blockCacheEnabled));
575   }
576 
577   /**
578    * @return bloom filter type used for new StoreFiles in ColumnFamily
579    */
580   public StoreFile.BloomType getBloomFilterType() {
581     String n = getValue(BLOOMFILTER);
582     if (n == null) {
583       n = DEFAULT_BLOOMFILTER;
584     }
585     return StoreFile.BloomType.valueOf(n.toUpperCase());
586   }
587 
588   /**
589    * @param bt bloom filter type
590    */
591   public void setBloomFilterType(final StoreFile.BloomType bt) {
592     setValue(BLOOMFILTER, bt.toString());
593   }
594 
595    /**
596     * @return the scope tag
597     */
598   public int getScope() {
599     String value = getValue(REPLICATION_SCOPE);
600     if (value != null) {
601       return Integer.valueOf(value).intValue();
602     }
603     return DEFAULT_REPLICATION_SCOPE;
604   }
605 
606  /**
607   * @param scope the scope tag
608   */
609   public void setScope(int scope) {
610     setValue(REPLICATION_SCOPE, Integer.toString(scope));
611   }
612 
613   /**
614    * @see java.lang.Object#toString()
615    */
616   @Override
617   public String toString() {
618     StringBuilder s = new StringBuilder();
619     s.append('{');
620     s.append(HConstants.NAME);
621     s.append(" => '");
622     s.append(Bytes.toString(name));
623     s.append("'");
624     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
625         values.entrySet()) {
626       String key = Bytes.toString(e.getKey().get());
627       String value = Bytes.toString(e.getValue().get());
628       s.append(", ");
629       s.append(key);
630       s.append(" => '");
631       s.append(value);
632       s.append("'");
633     }
634     s.append('}');
635     return s.toString();
636   }
637 
638   /**
639    * @return Column family descriptor with only the customized attributes.
640    */
641   public String toStringCustomizedValues() {
642     StringBuilder s = new StringBuilder();
643     s.append('{');
644     s.append(HConstants.NAME);
645     s.append(" => '");
646     s.append(Bytes.toString(name));
647     s.append("'");
648     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
649         values.entrySet()) {
650       String key = Bytes.toString(e.getKey().get());
651       String value = Bytes.toString(e.getValue().get());
652       if(DEFAULT_VALUES.get(key) == null || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
653         s.append(", ");
654         s.append(key);
655         s.append(" => '");
656         s.append(value);
657         s.append("'");
658       }
659     }
660     s.append('}');
661     return s.toString();
662   }
663 
664   /**
665    * @see java.lang.Object#equals(java.lang.Object)
666    */
667   @Override
668   public boolean equals(Object obj) {
669     if (this == obj) {
670       return true;
671     }
672     if (obj == null) {
673       return false;
674     }
675     if (!(obj instanceof HColumnDescriptor)) {
676       return false;
677     }
678     return compareTo((HColumnDescriptor)obj) == 0;
679   }
680 
681   /**
682    * @see java.lang.Object#hashCode()
683    */
684   @Override
685   public int hashCode() {
686     int result = Bytes.hashCode(this.name);
687     result ^= Byte.valueOf(COLUMN_DESCRIPTOR_VERSION).hashCode();
688     result ^= values.hashCode();
689     return result;
690   }
691 
692   // Writable
693 
694   public void readFields(DataInput in) throws IOException {
695     int version = in.readByte();
696     if (version < 6) {
697       if (version <= 2) {
698         Text t = new Text();
699         t.readFields(in);
700         this.name = t.getBytes();
701 //        if(KeyValue.getFamilyDelimiterIndex(this.name, 0, this.name.length)
702 //            > 0) {
703 //          this.name = stripColon(this.name);
704 //        }
705       } else {
706         this.name = Bytes.readByteArray(in);
707       }
708       this.values.clear();
709       setMaxVersions(in.readInt());
710       int ordinal = in.readInt();
711       setCompressionType(Compression.Algorithm.values()[ordinal]);
712       setInMemory(in.readBoolean());
713       setBloomFilterType(in.readBoolean() ? BloomType.ROW : BloomType.NONE);
714       if (getBloomFilterType() != BloomType.NONE && version < 5) {
715         // If a bloomFilter is enabled and the column descriptor is less than
716         // version 5, we need to skip over it to read the rest of the column
717         // descriptor. There are no BloomFilterDescriptors written to disk for
718         // column descriptors with a version number >= 5
719         throw new UnsupportedClassVersionError(this.getClass().getName() +
720             " does not support backward compatibility with versions older " +
721             "than version 5");
722       }
723       if (version > 1) {
724         setBlockCacheEnabled(in.readBoolean());
725       }
726       if (version > 2) {
727        setTimeToLive(in.readInt());
728       }
729     } else {
730       // version 6+
731       this.name = Bytes.readByteArray(in);
732       this.values.clear();
733       int numValues = in.readInt();
734       for (int i = 0; i < numValues; i++) {
735         ImmutableBytesWritable key = new ImmutableBytesWritable();
736         ImmutableBytesWritable value = new ImmutableBytesWritable();
737         key.readFields(in);
738         value.readFields(in);
739 
740         // in version 8, the BloomFilter setting changed from bool to enum
741         if (version < 8 && Bytes.toString(key.get()).equals(BLOOMFILTER)) {
742           value.set(Bytes.toBytes(
743               Boolean.getBoolean(Bytes.toString(value.get()))
744                 ? BloomType.ROW.toString()
745                 : BloomType.NONE.toString()));
746         }
747 
748         values.put(key, value);
749       }
750       if (version == 6) {
751         // Convert old values.
752         setValue(COMPRESSION, Compression.Algorithm.NONE.getName());
753       }
754       String value = getValue(HConstants.VERSIONS);
755       this.cachedMaxVersions = (value != null)?
756           Integer.valueOf(value).intValue(): DEFAULT_VERSIONS;
757     }
758   }
759 
760   public void write(DataOutput out) throws IOException {
761     out.writeByte(COLUMN_DESCRIPTOR_VERSION);
762     Bytes.writeByteArray(out, this.name);
763     out.writeInt(values.size());
764     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
765         values.entrySet()) {
766       e.getKey().write(out);
767       e.getValue().write(out);
768     }
769   }
770 
771   // Comparable
772 
773   public int compareTo(HColumnDescriptor o) {
774     int result = Bytes.compareTo(this.name, o.getName());
775     if (result == 0) {
776       // punt on comparison for ordering, just calculate difference
777       result = this.values.hashCode() - o.values.hashCode();
778       if (result < 0)
779         result = -1;
780       else if (result > 0)
781         result = 1;
782     }
783     return result;
784   }
785 }