001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.types;
019
020import java.util.Iterator;
021import java.util.NoSuchElementException;
022import org.apache.hadoop.hbase.util.PositionedByteRange;
023import org.apache.yetus.audience.InterfaceAudience;
024
025/**
026 * An {@link Iterator} over encoded {@code Struct} members.
027 * <p>
028 * This iterates over each serialized {@code Struct} field from the specified {@code DataTypes<?>[]}
029 * definition. It allows you to read the field or skip over its serialized bytes using
030 * {@link #next()} and {@link #skip()}, respectively. This is in contrast to the {@code Struct}
031 * method which allow you to {@link Struct#decode(PositionedByteRange)} or
032 * {@link Struct#skip(PositionedByteRange)} over the entire {@code Struct} at once.
033 * </p>
034 * <p>
035 * This iterator may also be used to read bytes from any {@code Struct} for which the specified
036 * {@code DataType<?>[]} is a prefix. For example, if the specified {@code Struct} definition has a
037 * {@link RawInteger} and a {@link RawStringTerminated} field, you may parse the serialized output
038 * of a {@code Struct} whose fields are {@link RawInteger}, {@link RawStringTerminated}, and
039 * {@link RawBytes}. The iterator would return a number followed by a {@code String}. The trailing
040 * {@code byte[]} would be ignored.
041 * </p>
042 */
043@InterfaceAudience.Public
044public class StructIterator implements Iterator<Object> {
045
046  protected final PositionedByteRange src;
047  protected int idx = 0;
048  @SuppressWarnings("rawtypes")
049  protected final DataType[] types;
050
051  /**
052   * Construct {@code StructIterator} over the values encoded in {@code src} using the specified
053   * {@code types} definition.
054   * @param src   The buffer from which to read encoded values.
055   * @param types The sequence of types to use as the schema for this {@code Struct}.
056   */
057  public StructIterator(PositionedByteRange src, @SuppressWarnings("rawtypes") DataType[] types) {
058    this.src = src;
059    this.types = types;
060  }
061
062  @Override
063  public boolean hasNext() {
064    // hasNext can return true when position == length in the case of a
065    // nullable field trailing a struct.
066    return idx < types.length && src.getPosition() <= src.getLength();
067  }
068
069  @Override
070  public void remove() {
071    throw new UnsupportedOperationException();
072  }
073
074  @Override
075  public Object next() {
076    if (!hasNext()) {
077      throw new NoSuchElementException();
078    }
079    DataType<?> t = types[idx++];
080    if (src.getPosition() == src.getLength() && t.isNullable()) {
081      return null;
082    }
083    return t.decode(src);
084  }
085
086  /**
087   * Bypass the next encoded value.
088   * @return the number of bytes skipped.
089   */
090  public int skip() {
091    if (!hasNext()) {
092      throw new NoSuchElementException();
093    }
094    DataType<?> t = types[idx++];
095    if (src.getPosition() == src.getLength() && t.isNullable()) {
096      return 0;
097    }
098    return t.skip(src);
099  }
100}