All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.wire.QueryWire Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016-2020 chronicle.software
 *
 *       https://chronicle.software
 *
 * 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 net.openhft.chronicle.wire;

import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.bytes.BytesStore;
import net.openhft.chronicle.bytes.StopCharTester;
import net.openhft.chronicle.bytes.ref.BinaryLongArrayReference;
import net.openhft.chronicle.core.annotation.ForceInline;
import net.openhft.chronicle.core.io.InvalidMarshallableException;
import net.openhft.chronicle.core.scoped.ScopedResource;
import net.openhft.chronicle.core.util.StringUtils;
import net.openhft.chronicle.core.values.IntArrayValues;
import net.openhft.chronicle.core.values.IntValue;
import net.openhft.chronicle.core.values.LongArrayValues;
import net.openhft.chronicle.core.values.LongValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Type;
import java.util.Base64;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;

/**
 * This class represents a wire format designed to decode URL query strings.
 * It extends the TextWire class and provides custom implementations for reading fields and consuming padding
 * in the context of URL query strings. Specialized value input and output handlers, namely {@code QueryValueIn}
 * and {@code QueryValueOut}, are used to manage the specific intricacies of the query string format.
 */
@SuppressWarnings("rawtypes")
public class QueryWire extends TextWire {

    // The specialized output handler for query string values.
    final QueryValueOut valueOut = new QueryValueOut();

    // The specialized input handler for query string values.
    final QueryValueIn valueIn = new QueryValueIn();

    /**
     * Constructs a new instance of {@code QueryWire} using the provided bytes.
     *
     * @param bytes The bytes that represent the query string to be processed.
     */
    public QueryWire(@NotNull Bytes bytes) {
        super(bytes);
    }

    @NotNull
    @Override
    protected QueryValueOut createValueOut() {
        return new QueryValueOut();
    }

    @NotNull
    @Override
    protected TextValueIn createValueIn() {
        return new QueryValueIn();
    }

    @Override
    @NotNull
    protected StringBuilder readField(@NotNull StringBuilder sb) {
        consumePadding();
        bytes.parseUtf8(sb, QueryStopCharTesters.QUERY_FIELD_NAME);
        if (rewindAndRead() == '&')
            bytes.readSkip(-1);
        return sb;
    }

    @Override
    @ForceInline
    public void consumePadding() {
        int codePoint = peekCode();
        while (Character.isWhitespace(codePoint)) {
            bytes.readSkip(1);
            codePoint = peekCode();
        }
    }

    @NotNull
    @Override
    public ValueOut write() {
        throw new UnsupportedOperationException();
    }

    @NotNull
    @Override
    public ValueOut write(@NotNull WireKey key) {
        return valueOut.write(key);
    }

    @NotNull
    @Override
    public ValueOut write(@NotNull CharSequence name) {
        return valueOut.write(name);
    }

    @NotNull
    @Override
    public ValueOut getValueOut() {
        return valueOut;
    }

    @Override
    public @NotNull ValueIn getValueIn() {
        return valueIn;
    }

    @NotNull
    @Override
    public QueryWire writeComment(@NotNull CharSequence s) {
        return this;
    }

    @NotNull
    @Override
    public QueryWire addPadding(int paddingToAdd) {
        return this;
    }

    /**
     * Reads an unsigned byte at the current position minus one.
     *
     * @return The unsigned byte value at the position one byte before the current read position.
     */
    int rewindAndRead() {
        return bytes.readUnsignedByte(bytes.readPosition() - 1);
    }

    @NotNull
    @Override
    public LongValue newLongReference() {
        throw new UnsupportedOperationException();
    }

    @NotNull
    @Override
    public IntValue newIntReference() {
        throw new UnsupportedOperationException();
    }

    @NotNull
    @Override
    public BinaryLongArrayReference newLongArrayReference() {
        throw new UnsupportedOperationException();
    }

    @Override
    public @NotNull IntArrayValues newIntArrayReference() {
        throw new UnsupportedOperationException();
    }

    /**
     * An enumeration defining testers for stopping characters when parsing fields and values
     * in the context of URL query strings.
     */
    enum QueryStopCharTesters implements StopCharTester {

        /**
         * Tester for identifying stopping characters when parsing a field name in a query string.
         * The method returns true if the character represents an '&' (delimiter) or '=' (assignment)
         * or if the character is a negative value (end of input).
         */
        QUERY_FIELD_NAME {
            @Override
            public boolean isStopChar(int ch) throws IllegalStateException {
                return ch == '&' || ch == '=' || ch < 0;
            }
        },

        /**
         * Tester for identifying stopping characters when parsing a value in a query string.
         * The method returns true if the character represents an '&' (delimiter)
         * or if the character is a negative value (end of input).
         */
        QUERY_VALUE {
            @Override
            public boolean isStopChar(int ch) throws IllegalStateException {
                return ch == '&' || ch < 0;
            }
        }
    }

    /**
     * Represents an output handler specialized for writing values in the context of URL query strings.
     * This class extends the {@code YamlValueOut} and provides custom implementations for
     * appending values to the wire with appropriate separators and field assignments.
     */
    class QueryValueOut extends YamlValueOut {

        // The separator to prepend before writing the next value.
        @NotNull
        String sep = "";

        // The field name to prepend before writing the next value.
        @Nullable
        CharSequence fieldName = null;

        @Override
        void prependSeparator() {
            bytes.appendUtf8(sep);
            sep = "";
            if (fieldName != null) {
                bytes.appendUtf8(fieldName).appendUtf8('=');
                fieldName = null;
            }
        }

        @Override
        public void elementSeparator() {
            sep = "&";
        }

        @NotNull
        @Override
        public QueryWire bool(@Nullable Boolean flag) {
            if (flag != null) {
                prependSeparator();
                bytes.appendUtf8(flag ? "true" : "false");
                elementSeparator();
            }
            return QueryWire.this;
        }

        @NotNull
        @Override
        public QueryWire text(@Nullable CharSequence s) {
            if (s != null) {
                prependSeparator();
                bytes.appendUtf8(s);
                elementSeparator();
            }
            return QueryWire.this;
        }

        @NotNull
        @Override
        public QueryWire int8(byte i8) {
            prependSeparator();
            bytes.appendUtf8(i8);
            elementSeparator();
            return QueryWire.this;
        }

        @NotNull
        @Override
        public QueryWire bytes(@Nullable BytesStore fromBytes) {
            throw new UnsupportedOperationException("todo");
        }

        @NotNull
        @Override
        public QueryWire rawBytes(@Nullable byte[] value) {
            if (value != null) {
                prependSeparator();
                bytes.write(value);
                elementSeparator();
            }
            return QueryWire.this;
        }

        @NotNull
        @Override
        public QueryWire bytes(byte[] byteArray) {
            prependSeparator();
            bytes.appendUtf8(Base64.getEncoder().encodeToString(byteArray));
            elementSeparator();

            return QueryWire.this;
        }

        @NotNull
        @Override
        public QueryWire int64array(long capacity) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryWire int64array(long capacity, @NotNull LongArrayValues values) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryValueOut typePrefix(@NotNull CharSequence typeName) {
            prependSeparator();
            bytes.appendUtf8(typeName);
            sep = " ";
            return this;
        }

        @NotNull
        @Override
        public QueryWire typeLiteral(@Nullable CharSequence type) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryWire typeLiteral(@NotNull BiConsumer> typeTranslator, @NotNull Class type) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryWire int32forBinding(int value) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryWire int32forBinding(int value, @NotNull IntValue intValue) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryWire int64forBinding(long value) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public QueryWire int64forBinding(long value, @NotNull LongValue longValue) {
            throw new UnsupportedOperationException();
        }

        @NotNull
        @Override
        public  QueryWire sequence(T t, @NotNull BiConsumer writer) {
            prependSeparator();
            pushState();
            bytes.appendUtf8("[");
            sep = ",";
            long pos = bytes.writePosition();
            writer.accept(t, this);
            if (pos != bytes.writePosition())
                bytes.appendUtf8(",");

            popState();
            bytes.appendUtf8("]");
            elementSeparator();
            return QueryWire.this;
        }

        @NotNull
        @Override
        public  QueryWire sequence(T t, K kls, @NotNull TriConsumer writer) throws InvalidMarshallableException {
            prependSeparator();
            pushState();
            bytes.appendUtf8("[");
            sep = ",";
            long pos = bytes.writePosition();
            writer.accept(t, kls, this);
            if (pos != bytes.writePosition())
                bytes.appendUtf8(",");

            popState();
            bytes.appendUtf8("]");
            elementSeparator();
            return QueryWire.this;
        }

        @Override
        protected void popState() {
        }

        @Override
        protected void pushState() {
        }

        @NotNull
        @Override
        public QueryWire marshallable(@NotNull WriteMarshallable object) throws InvalidMarshallableException {
            pushState();

            prependSeparator();
            bytes.appendUtf8("{");
            sep = ",";

            object.writeMarshallable(QueryWire.this);

            popState();

            bytes.appendUtf8('}');
            elementSeparator();
            return QueryWire.this;
        }

        @NotNull
        @Override
        public QueryWire map(@NotNull final Map map) {
            throw new UnsupportedOperationException();
        }

        @Override
        @NotNull
        public QueryValueOut write() {
            throw new UnsupportedOperationException();
        }

        @Override
        @NotNull
        public QueryValueOut write(@NotNull WireKey key) {
            fieldName = key.name();
            return this;
        }

        @Override
        @NotNull
        public QueryValueOut write(@NotNull CharSequence name) {
            fieldName = name;
            return this;
        }
    }

    /**
     * Represents an input handler specialized for reading values in the context of URL query strings.
     * This class extends the {@code TextValueIn} and provides custom implementations for
     * extracting text values from the wire with appropriate handling of separators and encodings.
     */
    class QueryValueIn extends TextValueIn {
        @Override
        public String text() {
            try (ScopedResource stlSb = Wires.acquireStringBuilderScoped()) {
                return StringUtils.toString(textTo(stlSb.get()));
            }
        }

        @Nullable
        @Override
        public StringBuilder textTo(@NotNull StringBuilder a) {
            consumePadding();
            bytes.parseUtf8(a, QueryStopCharTesters.QUERY_VALUE);
            return a;
        }

        @Nullable
        @Override
        public Bytes textTo(@NotNull Bytes a) {
            consumePadding();
            bytes.parseUtf8(a, QueryStopCharTesters.QUERY_VALUE);
            return a;
        }

        @Override
        @NotNull
        public  WireIn typeLiteralAsText(T t, @NotNull BiConsumer classNameConsumer) {
            try (ScopedResource stlSb = Wires.acquireStringBuilderScoped()) {
                StringBuilder sb = stlSb.get();
                textTo(sb);
                classNameConsumer.accept(t, sb);
            }
            return wireIn();
        }

        @Override
        public Type typeLiteral(BiFunction unresolvedHandler) {
            try (ScopedResource stlSb = Wires.acquireStringBuilderScoped()) {
                StringBuilder sb = stlSb.get();
                textTo(sb);
                return classLookup().forName(sb);
            }
        }

        @Override
        public boolean hasNextSequenceItem() {
            consumePadding();
            int ch = peekCode();
            if (ch == ',') {
                bytes.readSkip(1);
                return true;
            }
            return ch != ']';
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy