com.jeffplaisance.util.fingertree.bytestring.FingerTreeByteString.java Source code

Java tutorial

Introduction

Here is the source code for com.jeffplaisance.util.fingertree.bytestring.FingerTreeByteString.java

Source

/*
 * Copyright 2013 Jeff Plaisance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jeffplaisance.util.fingertree.bytestring;

import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.io.InputSupplier;
import com.jeffplaisance.util.Pair;
import com.jeffplaisance.util.fingertree.Empty;
import com.jeffplaisance.util.fingertree.FingerTree;
import com.jeffplaisance.util.fingertree.Measured;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

class FingerTreeByteString extends ByteString {

    public static final Measured<Integer, ByteStringLiteral> BYTE_STRING_LENGTH_MEASURE = new Measured<Integer, ByteStringLiteral>() {
        @Override
        public Integer sum(Integer a, Integer b) {
            return a + b;
        }

        @Override
        public Integer measure(ByteStringLiteral a) {
            return a.length();
        }

        @Override
        public Integer zero() {
            return 0;
        }
    };

    static FingerTreeByteString emptyFT() {
        return new FingerTreeByteString(new Empty<Integer, ByteStringLiteral>(BYTE_STRING_LENGTH_MEASURE));
    }

    final FingerTree<Integer, ByteStringLiteral> bytes;

    public FingerTreeByteString(FingerTree<Integer, ByteStringLiteral> bytes) {
        this.bytes = bytes;
    }

    public ByteString prepend(ByteStringLiteral literal) {
        if (!bytes.isEmpty() && literal.length() + bytes.first().length() <= ByteStringLiteral.COPY_THRESHOLD) {
            return new FingerTreeByteString(
                    bytes.removeFirst().addFirst(ByteStringLiteral.copy(literal, bytes.first())));
        } else {
            return new FingerTreeByteString(bytes.addFirst(literal));
        }
    }

    public ByteString append(ByteStringLiteral literal) {
        if (!bytes.isEmpty() && bytes.last().length() + literal.length() <= ByteStringLiteral.COPY_THRESHOLD) {
            return new FingerTreeByteString(
                    bytes.removeLast().addLast(ByteStringLiteral.copy(bytes.last(), literal)));
        } else {
            return new FingerTreeByteString(bytes.addLast(literal));
        }
    }

    @Override
    public ByteString concat(ByteString other) {
        if (!other.isLiteral()) {
            return new FingerTreeByteString(bytes.concat(((FingerTreeByteString) other).bytes));
        } else {
            return append((ByteStringLiteral) other);
        }
    }

    @Override
    public ByteString substring(final int start, final int end) {
        final Pair<FingerTree<Integer, ByteStringLiteral>, FingerTree<Integer, ByteStringLiteral>> split1 = bytes
                .split(new Predicate<Integer>() {
                    @Override
                    public boolean apply(Integer integer) {
                        return integer > start;
                    }
                });
        final int headLength = split1.a().measure();
        final FingerTree<Integer, ByteStringLiteral> substring = split1.b().takeUntil(new Predicate<Integer>() {
            @Override
            public boolean apply(Integer integer) {
                return integer >= end - headLength;
            }
        }, true);
        final int substringLen = substring.measure();
        final FingerTree<Integer, ByteStringLiteral> substring2 = substring.removeFirst()
                .addFirst(substring.first().substring(start - headLength, substring.first().length()));
        final FingerTree<Integer, ByteStringLiteral> substring3 = substring2.removeLast().addLast(
                substring2.last().substring(0, substring2.last().length() - (headLength + substringLen - end)));
        return new FingerTreeByteString(substring3);
    }

    @Override
    public ByteString substring(int start) {
        return substring(start, length());
    }

    @Override
    public InputStream newInput() {
        try {
            return ByteStreams
                    .join(Iterables.transform(bytes, new Function<ByteStringLiteral, InputSupplier<InputStream>>() {
                        @Override
                        public InputSupplier<InputStream> apply(final ByteStringLiteral literal) {
                            return new InputSupplier<InputStream>() {
                                @Override
                                public InputStream getInput() throws IOException {
                                    return literal.newInput();
                                }
                            };
                        }
                    })).getInput();
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    @Override
    public void writeTo(OutputStream out) throws IOException {
        for (ByteStringLiteral literal : bytes) {
            literal.writeTo(out);
        }
    }

    @Override
    public byte getByte(final int index) {
        final Pair<FingerTree<Integer, ByteStringLiteral>, FingerTree<Integer, ByteStringLiteral>> split = bytes
                .split(new Predicate<Integer>() {
                    @Override
                    public boolean apply(Integer integer) {
                        return integer > index;
                    }
                });
        return split.b().first().getByte(index - split.a().measure());
    }

    @Override
    public int length() {
        return bytes.measure();
    }

    @Override
    boolean isLiteral() {
        return false;
    }
}