View Javadoc

1   /*
2    * Copyright 2010 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  
21  package org.apache.hadoop.hbase.rest.model;
22  
23  import java.io.IOException;
24  import java.io.Serializable;
25  import java.io.StringReader;
26  import java.io.StringWriter;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.NavigableSet;
31  
32  import javax.xml.bind.annotation.XmlAttribute;
33  import javax.xml.bind.annotation.XmlElement;
34  import javax.xml.bind.annotation.XmlRootElement;
35  
36  import org.apache.hadoop.hbase.HConstants;
37  import org.apache.hadoop.hbase.client.Scan;
38  import org.apache.hadoop.hbase.filter.*;
39  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
40  import org.apache.hadoop.hbase.rest.ProtobufMessageHandler;
41  import org.apache.hadoop.hbase.rest.protobuf.generated.ScannerMessage.Scanner;
42  import org.apache.hadoop.hbase.util.Base64;
43  import org.apache.hadoop.hbase.util.Bytes;
44  
45  import com.google.protobuf.ByteString;
46  
47  import com.sun.jersey.api.json.JSONConfiguration;
48  import com.sun.jersey.api.json.JSONJAXBContext;
49  import com.sun.jersey.api.json.JSONMarshaller;
50  import com.sun.jersey.api.json.JSONUnmarshaller;
51  
52  /**
53   * A representation of Scanner parameters.
54   * 
55   * <pre>
56   * &lt;complexType name="Scanner"&gt;
57   *   &lt;sequence>
58   *     &lt;element name="column" type="base64Binary" minOccurs="0" maxOccurs="unbounded"/&gt;
59   *     &lt;element name="filter" type="string" minOccurs="0" maxOccurs="1"&gt;&lt;/element&gt;
60   *   &lt;/sequence&gt;
61   *   &lt;attribute name="startRow" type="base64Binary"&gt;&lt;/attribute&gt;
62   *   &lt;attribute name="endRow" type="base64Binary"&gt;&lt;/attribute&gt;
63   *   &lt;attribute name="batch" type="int"&gt;&lt;/attribute&gt;
64   *   &lt;attribute name="startTime" type="int"&gt;&lt;/attribute&gt;
65   *   &lt;attribute name="endTime" type="int"&gt;&lt;/attribute&gt;
66   *   &lt;attribute name="maxVersions" type="int"&gt;&lt;/attribute&gt;
67   * &lt;/complexType&gt;
68   * </pre>
69   */
70  @XmlRootElement(name="Scanner")
71  public class ScannerModel implements ProtobufMessageHandler, Serializable {
72  
73    private static final long serialVersionUID = 1L;
74  
75    private byte[] startRow = HConstants.EMPTY_START_ROW;
76    private byte[] endRow = HConstants.EMPTY_END_ROW;;
77    private List<byte[]> columns = new ArrayList<byte[]>();
78    private int batch = Integer.MAX_VALUE;
79    private long startTime = 0;
80    private long endTime = Long.MAX_VALUE;
81    private String filter = null;
82    private int maxVersions = Integer.MAX_VALUE;
83  
84    @XmlRootElement
85    static class FilterModel {
86      
87      @XmlRootElement
88      static class WritableByteArrayComparableModel {
89        @XmlAttribute public String type;
90        @XmlAttribute public String value;
91        @XmlAttribute public String op;
92  
93        static enum ComparatorType {
94          BinaryComparator,
95          BinaryPrefixComparator,
96          BitComparator,
97          NullComparator,
98          RegexStringComparator,
99          SubstringComparator    
100       }
101 
102       public WritableByteArrayComparableModel() { }
103 
104       public WritableByteArrayComparableModel(
105           WritableByteArrayComparable comparator) {
106         String typeName = comparator.getClass().getSimpleName();
107         ComparatorType type = ComparatorType.valueOf(typeName);
108         this.type = typeName;
109         switch (type) {
110           case BinaryComparator:
111           case BinaryPrefixComparator:
112             this.value = Base64.encodeBytes(comparator.getValue());
113             break;
114           case BitComparator:
115             this.value = Base64.encodeBytes(comparator.getValue());
116             this.op = ((BitComparator)comparator).getOperator().toString();
117             break;
118           case NullComparator:
119             break;
120           case RegexStringComparator:
121           case SubstringComparator:
122             this.value = Bytes.toString(comparator.getValue());
123             break;
124           default:
125             throw new RuntimeException("unhandled filter type: " + type);
126         }
127       }
128 
129       public WritableByteArrayComparable build() {
130         WritableByteArrayComparable comparator;
131         switch (ComparatorType.valueOf(type)) {
132           case BinaryComparator:
133             comparator = new BinaryComparator(Base64.decode(value));
134             break;
135           case BinaryPrefixComparator:
136             comparator = new BinaryPrefixComparator(Base64.decode(value));
137             break;
138           case BitComparator:
139             comparator = new BitComparator(Base64.decode(value),
140                 BitComparator.BitwiseOp.valueOf(op));
141             break;
142           case NullComparator:
143             comparator = new NullComparator();
144             break;
145           case RegexStringComparator:
146             comparator = new RegexStringComparator(value);
147             break;
148           case SubstringComparator:
149             comparator = new SubstringComparator(value);
150             break;
151           default:
152             throw new RuntimeException("unhandled comparator type: " + type);
153         }
154         return comparator;
155       }
156 
157     }
158 
159     // A grab bag of fields, would have been a union if this were C.
160     // These are null by default and will only be serialized if set (non null).
161     @XmlAttribute public String type;
162     @XmlAttribute public String op;
163     @XmlElement WritableByteArrayComparableModel comparator;
164     @XmlAttribute public String value;
165     @XmlElement public List<FilterModel> filters;
166     @XmlAttribute public Integer limit;
167     @XmlAttribute public Integer offset;
168     @XmlAttribute public String family;
169     @XmlAttribute public String qualifier;
170     @XmlAttribute public Boolean ifMissing;
171     @XmlAttribute public Boolean latestVersion;
172     @XmlAttribute public String minColumn;
173     @XmlAttribute public Boolean minColumnInclusive;
174     @XmlAttribute public String maxColumn;
175     @XmlAttribute public Boolean maxColumnInclusive;
176     @XmlAttribute public Boolean dropDependentColumn;
177     @XmlAttribute public Float chance;
178     @XmlElement public List<String> prefixes;
179     @XmlElement public List<Long> timestamps;
180 
181     static enum FilterType {
182       ColumnCountGetFilter,
183       ColumnPaginationFilter,
184       ColumnPrefixFilter,
185       ColumnRangeFilter,
186       DependentColumnFilter,
187       FamilyFilter,
188       FilterList,
189       FirstKeyOnlyFilter,
190       InclusiveStopFilter,
191       KeyOnlyFilter,
192       MultipleColumnPrefixFilter,
193       PageFilter,
194       PrefixFilter,
195       QualifierFilter,
196       RandomRowFilter,
197       RowFilter,
198       SingleColumnValueExcludeFilter,
199       SingleColumnValueFilter,
200       SkipFilter,
201       TimestampsFilter,
202       ValueFilter,
203       WhileMatchFilter    
204     }
205 
206     public FilterModel() { }
207     
208     public FilterModel(Filter filter) { 
209       String typeName = filter.getClass().getSimpleName();
210       FilterType type = FilterType.valueOf(typeName);
211       this.type = typeName;
212       switch (type) {
213         case ColumnCountGetFilter:
214           this.limit = ((ColumnCountGetFilter)filter).getLimit();
215           break;
216         case ColumnPaginationFilter:
217           this.limit = ((ColumnPaginationFilter)filter).getLimit();
218           this.offset = ((ColumnPaginationFilter)filter).getOffset();
219           break;
220         case ColumnPrefixFilter:
221           this.value = Base64.encodeBytes(((ColumnPrefixFilter)filter).getPrefix());
222           break;
223         case ColumnRangeFilter:
224           this.minColumn = Base64.encodeBytes(((ColumnRangeFilter)filter).getMinColumn());
225           this.minColumnInclusive = ((ColumnRangeFilter)filter).getMinColumnInclusive();
226           this.maxColumn = Base64.encodeBytes(((ColumnRangeFilter)filter).getMaxColumn());
227           this.maxColumnInclusive = ((ColumnRangeFilter)filter).getMaxColumnInclusive();
228           break;
229         case DependentColumnFilter: {
230           DependentColumnFilter dcf = (DependentColumnFilter)filter;
231           this.family = Base64.encodeBytes(dcf.getFamily());
232           byte[] qualifier = dcf.getQualifier();
233           if (qualifier != null) {
234             this.qualifier = Base64.encodeBytes(qualifier);
235           }
236           this.op = dcf.getOperator().toString();
237           this.comparator = new WritableByteArrayComparableModel(dcf.getComparator());
238           this.dropDependentColumn = dcf.dropDependentColumn();
239         } break;
240         case FilterList:
241           this.op = ((FilterList)filter).getOperator().toString();
242           this.filters = new ArrayList<FilterModel>();
243           for (Filter child: ((FilterList)filter).getFilters()) {
244             this.filters.add(new FilterModel(child));
245           }
246           break;
247         case FirstKeyOnlyFilter:
248         case KeyOnlyFilter:
249           break;
250         case InclusiveStopFilter:
251           this.value = 
252             Base64.encodeBytes(((InclusiveStopFilter)filter).getStopRowKey());
253           break;
254         case MultipleColumnPrefixFilter:
255           this.prefixes = new ArrayList<String>();
256           for (byte[] prefix: ((MultipleColumnPrefixFilter)filter).getPrefix()) {
257             this.prefixes.add(Base64.encodeBytes(prefix));
258           }
259           break;
260         case PageFilter:
261           this.value = Long.toString(((PageFilter)filter).getPageSize());
262           break;
263         case PrefixFilter:
264           this.value = Base64.encodeBytes(((PrefixFilter)filter).getPrefix());
265           break;
266         case FamilyFilter:
267         case QualifierFilter:
268         case RowFilter:
269         case ValueFilter:
270           this.op = ((CompareFilter)filter).getOperator().toString();
271           this.comparator = 
272             new WritableByteArrayComparableModel(
273               ((CompareFilter)filter).getComparator());
274           break;
275         case RandomRowFilter:
276           this.chance = ((RandomRowFilter)filter).getChance();
277           break;
278         case SingleColumnValueExcludeFilter:
279         case SingleColumnValueFilter: {
280           SingleColumnValueFilter scvf = (SingleColumnValueFilter) filter;
281           this.family = Base64.encodeBytes(scvf.getFamily());
282           byte[] qualifier = scvf.getQualifier();
283           if (qualifier != null) {
284             this.qualifier = Base64.encodeBytes(qualifier);
285           }
286           this.op = scvf.getOperator().toString();
287           this.comparator = 
288             new WritableByteArrayComparableModel(scvf.getComparator());
289           if (scvf.getFilterIfMissing()) {
290             this.ifMissing = true;
291           }
292           if (scvf.getLatestVersionOnly()) {
293             this.latestVersion = true;
294           }
295         } break;
296         case SkipFilter:
297           this.filters = new ArrayList<FilterModel>();
298           this.filters.add(new FilterModel(((SkipFilter)filter).getFilter()));
299           break;
300         case TimestampsFilter:
301           this.timestamps = ((TimestampsFilter)filter).getTimestamps();
302           break;
303         case WhileMatchFilter:
304           this.filters = new ArrayList<FilterModel>();
305           this.filters.add(
306             new FilterModel(((WhileMatchFilter)filter).getFilter()));
307           break;
308         default:
309           throw new RuntimeException("unhandled filter type " + type);
310       }
311     }
312 
313     public Filter build() {
314       Filter filter;
315       switch (FilterType.valueOf(type)) {
316       case ColumnCountGetFilter:
317         filter = new ColumnCountGetFilter(limit);
318         break;
319       case ColumnPaginationFilter:
320         filter = new ColumnPaginationFilter(limit, offset);
321         break;
322       case ColumnPrefixFilter:
323         filter = new ColumnPrefixFilter(Base64.decode(value));
324         break;
325       case ColumnRangeFilter:
326         filter = new ColumnRangeFilter(Base64.decode(minColumn),
327             minColumnInclusive, Base64.decode(maxColumn),
328             maxColumnInclusive);
329         break;
330       case DependentColumnFilter:
331         filter = new DependentColumnFilter(Base64.decode(family),
332             qualifier != null ? Base64.decode(qualifier) : null,
333             dropDependentColumn, CompareOp.valueOf(op), comparator.build());
334         break;
335       case FamilyFilter:
336         filter = new FamilyFilter(CompareOp.valueOf(op), comparator.build());
337         break;
338       case FilterList: {
339         List<Filter> list = new ArrayList<Filter>();
340         for (FilterModel model: filters) {
341           list.add(model.build());
342         }
343         filter = new FilterList(FilterList.Operator.valueOf(op), list);
344       } break;
345       case FirstKeyOnlyFilter:
346         filter = new FirstKeyOnlyFilter();
347         break;
348       case InclusiveStopFilter:
349         filter = new InclusiveStopFilter(Base64.decode(value));
350         break;
351       case KeyOnlyFilter:
352         filter = new KeyOnlyFilter();
353         break;
354       case MultipleColumnPrefixFilter: {
355         byte[][] values = new byte[prefixes.size()][];
356         for (int i = 0; i < prefixes.size(); i++) {
357           values[i] = Base64.decode(prefixes.get(i));
358         }
359         filter = new MultipleColumnPrefixFilter(values);
360       } break;
361       case PageFilter:
362         filter = new PageFilter(Long.valueOf(value));
363         break;
364       case PrefixFilter:
365         filter = new PrefixFilter(Base64.decode(value));
366         break;
367       case QualifierFilter:
368         filter = new QualifierFilter(CompareOp.valueOf(op), comparator.build());
369         break;
370       case RandomRowFilter:
371         filter = new RandomRowFilter(chance);
372         break;
373       case RowFilter:
374         filter = new RowFilter(CompareOp.valueOf(op), comparator.build());
375         break;
376       case SingleColumnValueFilter:
377         filter = new SingleColumnValueFilter(Base64.decode(family),
378           qualifier != null ? Base64.decode(qualifier) : null,
379           CompareOp.valueOf(op), comparator.build());
380         if (ifMissing != null) {
381           ((SingleColumnValueFilter)filter).setFilterIfMissing(ifMissing);
382         }
383         if (latestVersion != null) {
384           ((SingleColumnValueFilter)filter).setLatestVersionOnly(latestVersion);
385         }
386         break;
387       case SingleColumnValueExcludeFilter:
388         filter = new SingleColumnValueExcludeFilter(Base64.decode(family),
389           qualifier != null ? Base64.decode(qualifier) : null,
390           CompareOp.valueOf(op), comparator.build());
391         if (ifMissing != null) {
392           ((SingleColumnValueExcludeFilter)filter).setFilterIfMissing(ifMissing);
393         }
394         if (latestVersion != null) {
395           ((SingleColumnValueExcludeFilter)filter).setLatestVersionOnly(latestVersion);
396         }
397         break;
398       case SkipFilter:
399         filter = new SkipFilter(filters.get(0).build());
400         break;
401       case TimestampsFilter:
402         filter = new TimestampsFilter(timestamps);
403         break;
404       case ValueFilter:
405         filter = new ValueFilter(CompareOp.valueOf(op), comparator.build());
406         break;
407       case WhileMatchFilter:
408         filter = new WhileMatchFilter(filters.get(0).build());
409         break;
410       default:
411         throw new RuntimeException("unhandled filter type: " + type);
412       }
413       return filter;
414     }
415 
416   }
417 
418   /**
419    * @param s the JSON representation of the filter
420    * @return the filter
421    * @throws Exception
422    */
423   public static Filter buildFilter(String s) throws Exception {
424     JSONJAXBContext context =
425       new JSONJAXBContext(JSONConfiguration.natural().build(),
426         FilterModel.class);
427     JSONUnmarshaller unmarshaller = context.createJSONUnmarshaller();
428     FilterModel model = unmarshaller.unmarshalFromJSON(new StringReader(s),
429       FilterModel.class);
430     return model.build();
431   }
432 
433   /**
434    * @param filter the filter
435    * @return the JSON representation of the filter
436    * @throws Exception 
437    */
438   public static String stringifyFilter(final Filter filter) throws Exception {
439     JSONJAXBContext context =
440       new JSONJAXBContext(JSONConfiguration.natural().build(),
441         FilterModel.class);
442     JSONMarshaller marshaller = context.createJSONMarshaller();
443     StringWriter writer = new StringWriter();
444     marshaller.marshallToJSON(new FilterModel(filter), writer);
445     return writer.toString();
446   }
447 
448   private static final byte[] COLUMN_DIVIDER = Bytes.toBytes(":");
449 
450   /**
451    * @param scan the scan specification
452    * @throws Exception 
453    */
454   public static ScannerModel fromScan(Scan scan) throws Exception {
455     ScannerModel model = new ScannerModel();
456     model.setStartRow(scan.getStartRow());
457     model.setEndRow(scan.getStopRow());
458     Map<byte [], NavigableSet<byte []>> families = scan.getFamilyMap();
459     if (families != null) {
460       for (Map.Entry<byte [], NavigableSet<byte []>> entry : families.entrySet()) {
461         if (entry.getValue() != null) {
462           for (byte[] qualifier: entry.getValue()) {
463             model.addColumn(Bytes.add(entry.getKey(), COLUMN_DIVIDER, qualifier));
464           }
465         } else {
466           model.addColumn(entry.getKey());
467         }
468       }
469     }
470     model.setStartTime(scan.getTimeRange().getMin());
471     model.setEndTime(scan.getTimeRange().getMax());
472     int caching = scan.getCaching();
473     if (caching > 0) {
474       model.setBatch(caching);
475     }
476     int maxVersions = scan.getMaxVersions();
477     if (maxVersions > 0) {
478       model.setMaxVersions(maxVersions);
479     }
480     Filter filter = scan.getFilter();
481     if (filter != null) {
482       model.setFilter(stringifyFilter(filter));
483     }
484     return model;
485   }
486 
487   /**
488    * Default constructor
489    */
490   public ScannerModel() {}
491 
492   /**
493    * Constructor
494    * @param startRow the start key of the row-range
495    * @param endRow the end key of the row-range
496    * @param columns the columns to scan
497    * @param batch the number of values to return in batch
498    * @param endTime the upper bound on timestamps of values of interest
499    * @param maxVersions the maximum number of versions to return
500    * @param filter a filter specification
501    * (values with timestamps later than this are excluded)
502    */
503   public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns,
504       int batch, long endTime, int maxVersions, String filter) {
505     super();
506     this.startRow = startRow;
507     this.endRow = endRow;
508     this.columns = columns;
509     this.batch = batch;
510     this.endTime = endTime;
511     this.maxVersions = maxVersions;
512     this.filter = filter;
513   }
514 
515   /**
516    * Constructor 
517    * @param startRow the start key of the row-range
518    * @param endRow the end key of the row-range
519    * @param columns the columns to scan
520    * @param batch the number of values to return in batch
521    * @param startTime the lower bound on timestamps of values of interest
522    * (values with timestamps earlier than this are excluded)
523    * @param endTime the upper bound on timestamps of values of interest
524    * (values with timestamps later than this are excluded)
525    * @param filter a filter specification
526    */
527   public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns,
528       int batch, long startTime, long endTime, String filter) {
529     super();
530     this.startRow = startRow;
531     this.endRow = endRow;
532     this.columns = columns;
533     this.batch = batch;
534     this.startTime = startTime;
535     this.endTime = endTime;
536     this.filter = filter;
537   }
538 
539   /**
540    * Add a column to the column set
541    * @param column the column name, as &lt;column&gt;(:&lt;qualifier&gt;)?
542    */
543   public void addColumn(byte[] column) {
544     columns.add(column);
545   }
546 
547   /**
548    * @return true if a start row was specified
549    */
550   public boolean hasStartRow() {
551     return !Bytes.equals(startRow, HConstants.EMPTY_START_ROW);
552   }
553 
554   /**
555    * @return start row
556    */
557   @XmlAttribute
558   public byte[] getStartRow() {
559     return startRow;
560   }
561 
562   /**
563    * @return true if an end row was specified
564    */
565   public boolean hasEndRow() {
566     return !Bytes.equals(endRow, HConstants.EMPTY_END_ROW);
567   }
568 
569   /**
570    * @return end row
571    */
572   @XmlAttribute
573   public byte[] getEndRow() {
574     return endRow;
575   }
576 
577   /**
578    * @return list of columns of interest in column:qualifier format, or empty for all
579    */
580   @XmlElement(name="column")
581   public List<byte[]> getColumns() {
582     return columns;
583   }
584   
585   /**
586    * @return the number of cells to return in batch
587    */
588   @XmlAttribute
589   public int getBatch() {
590     return batch;
591   }
592 
593   /**
594    * @return the lower bound on timestamps of items of interest
595    */
596   @XmlAttribute
597   public long getStartTime() {
598     return startTime;
599   }
600 
601   /**
602    * @return the upper bound on timestamps of items of interest
603    */
604   @XmlAttribute
605   public long getEndTime() {
606     return endTime;
607   }
608 
609   /**
610    * @return maximum number of versions to return
611    */
612   @XmlAttribute
613   public int getMaxVersions() {
614     return maxVersions;
615   }
616 
617   /**
618    * @return the filter specification
619    */
620   @XmlElement
621   public String getFilter() {
622     return filter;
623   }
624 
625   /**
626    * @param startRow start row
627    */
628   public void setStartRow(byte[] startRow) {
629     this.startRow = startRow;
630   }
631 
632   /**
633    * @param endRow end row
634    */
635   public void setEndRow(byte[] endRow) {
636     this.endRow = endRow;
637   }
638 
639   /**
640    * @param columns list of columns of interest in column:qualifier format, or empty for all
641    */
642   public void setColumns(List<byte[]> columns) {
643     this.columns = columns;
644   }
645 
646   /**
647    * @param batch the number of cells to return in batch
648    */
649   public void setBatch(int batch) {
650     this.batch = batch;
651   }
652 
653   /**
654    * @param maxVersions maximum number of versions to return
655    */
656   public void setMaxVersions(int maxVersions) {
657     this.maxVersions = maxVersions;
658   }
659 
660   /**
661    * @param startTime the lower bound on timestamps of values of interest
662    */
663   public void setStartTime(long startTime) {
664     this.startTime = startTime;
665   }
666 
667   /**
668    * @param endTime the upper bound on timestamps of values of interest
669    */
670   public void setEndTime(long endTime) {
671     this.endTime = endTime;
672   }
673 
674   /**
675    * @param filter the filter specification
676    */
677   public void setFilter(String filter) {
678     this.filter = filter;
679   }
680 
681   @Override
682   public byte[] createProtobufOutput() {
683     Scanner.Builder builder = Scanner.newBuilder();
684     if (!Bytes.equals(startRow, HConstants.EMPTY_START_ROW)) {
685       builder.setStartRow(ByteString.copyFrom(startRow));
686     }
687     if (!Bytes.equals(endRow, HConstants.EMPTY_START_ROW)) {
688       builder.setEndRow(ByteString.copyFrom(endRow));
689     }
690     for (byte[] column: columns) {
691       builder.addColumns(ByteString.copyFrom(column));
692     }
693     builder.setBatch(batch);
694     if (startTime != 0) {
695       builder.setStartTime(startTime);
696     }
697     if (endTime != 0) {
698       builder.setEndTime(endTime);
699     }
700     builder.setBatch(getBatch());
701     builder.setMaxVersions(maxVersions);
702     if (filter != null) {
703       builder.setFilter(filter);
704     }
705     return builder.build().toByteArray();
706   }
707 
708   @Override
709   public ProtobufMessageHandler getObjectFromMessage(byte[] message)
710       throws IOException {
711     Scanner.Builder builder = Scanner.newBuilder();
712     builder.mergeFrom(message);
713     if (builder.hasStartRow()) {
714       startRow = builder.getStartRow().toByteArray();
715     }
716     if (builder.hasEndRow()) {
717       endRow = builder.getEndRow().toByteArray();
718     }
719     for (ByteString column: builder.getColumnsList()) {
720       addColumn(column.toByteArray());
721     }
722     if (builder.hasBatch()) {
723       batch = builder.getBatch();
724     }
725     if (builder.hasStartTime()) {
726       startTime = builder.getStartTime();
727     }
728     if (builder.hasEndTime()) {
729       endTime = builder.getEndTime();
730     }
731     if (builder.hasMaxVersions()) {
732       maxVersions = builder.getMaxVersions();
733     }
734     if (builder.hasFilter()) {
735       filter = builder.getFilter();
736     }
737     return this;
738   }
739 
740 }