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

net.openhft.chronicle.wire.VanillaWireParser 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.core.Jvm;
import net.openhft.chronicle.core.io.InvalidMarshallableException;
import net.openhft.chronicle.core.util.CharSequenceComparator;
import net.openhft.chronicle.core.util.InvocationTargetRuntimeException;
import net.openhft.chronicle.core.util.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * Provides an implementation of the WireParser interface, parsing wire inputs using both named and numbered
 * parselets to associate specific actions with events or field names.
 * 

* This parser uses a default consumer to handle unmatched entries and a field number parselet for numbered fields. */ public class VanillaWireParser implements WireParser { // Map of field names to their associated parselets, sorted by CharSequence order. private final Map namedConsumer = new TreeMap<>(CharSequenceComparator.INSTANCE); // Map of field numbers to their associated parselets. private final Map> numberedConsumer = new HashMap<>(); // The default consumer to handle unmatched entries. private final WireParselet defaultConsumer; // Used for building strings for parsing. private final StringBuilder sb = new StringBuilder(128); // Holds the name of the last event that was parsed. private final StringBuilder lastEventName = new StringBuilder(128); // Handles numbered fields. private FieldNumberParselet fieldNumberParselet; // Holds the last parslet that was used. private WireParselet lastParslet = null; // Indicates the position in the wire input of the last parsed event. private long lastStart = 0; /** * Constructs a new VanillaWireParser with the specified default consumer and field number parselet. * * @param defaultConsumer The default consumer to handle unmatched entries. * @param fieldNumberParselet The field number parselet for handling numbered fields. */ public VanillaWireParser(@NotNull WireParselet defaultConsumer, @NotNull FieldNumberParselet fieldNumberParselet) { this.defaultConsumer = defaultConsumer; // Initializing the lastEventName with a non-ASCII value to ensure uniqueness. lastEventName.appendCodePoint(0xFFFF); this.fieldNumberParselet = fieldNumberParselet; } /** * Peeks at the next code or byte in the wire input without moving the read position. * * @param wireIn The wire input to peek into. * @return The next unsigned byte as an integer. */ private int peekCode(@NotNull WireIn wireIn) { return wireIn.bytes().peekUnsignedByte(); } @Override public WireParselet getDefaultConsumer() { return defaultConsumer; } /** * Parses a single input from the wire. Determines if the input should be parsed as binary or not. * Throws specific exceptions in the case of invocation issues or invalid marshallable data. * * @param wireIn The wire input to parse. * @throws InvocationTargetRuntimeException if an invocation error occurs during parsing. * @throws InvalidMarshallableException if invalid marshallable data is encountered during parsing. */ public void parseOne(@NotNull WireIn wireIn) throws InvocationTargetRuntimeException, InvalidMarshallableException { long start = wireIn.bytes().readPosition(); // Check if it's binary data by peeking the code. if (peekCode(wireIn) == BinaryWireCode.FIELD_NUMBER) { parseOneBinary(wireIn); return; } @NotNull ValueIn valueIn = wireIn.readEventName(sb); WireParselet parslet; // Check if the event name is the same as the previous one to avoid unnecessary TreeMap lookup. if (StringUtils.isEqual(sb, lastEventName)) { parslet = lastParslet; } else { parslet = lookup(sb); // If the parselet wasn't found and the event name is empty, handle the empty event name. if (parslet == null) { if (sb.length() == 0) { parseOneEmpty(wireIn, start); } parslet = getDefaultConsumer(); } } parslet.accept(sb, valueIn); // Update the last event name, last parslet, and last start position for the next parse. lastEventName.setLength(0); lastEventName.append(sb); lastParslet = parslet; lastStart = start; } /** * Handles the scenario where an attempt to read a method name results in an empty value. * Logs a warning about the situation and the contents leading up to this. * * @param wireIn The wire input being parsed. * @param start The position in the wire input at which the method started. */ private void parseOneEmpty(@NotNull WireIn wireIn, long start) { // Log a warning message indicating a potential misplaced method. Jvm.warn().on(getClass(), "Attempt to read method name/id but not at the start of a method, the previous method name was " + lastEventName + "\n" + wireIn.bytes().toHexString(start, 1024)); if (lastStart < start && lastStart + 1024 >= start) Jvm.warn().on(getClass(), "The previous message was\n" + wireIn.bytes().toHexString(lastStart, start - lastStart)); } /** * Parses binary data from the wire based on a method ID. If the ID is not mapped, the * field number parselet is used to continue the parsing process. * * @param wireIn The wire input containing binary data. * @throws InvalidMarshallableException if invalid marshallable data is encountered. */ private void parseOneBinary(@NotNull WireIn wireIn) throws InvalidMarshallableException { long methodId = wireIn.readEventNumber(); // Check if methodId is mapped in the numberedConsumer. if (methodId == (int) methodId) { Map.Entry entry = numberedConsumer.get((int) methodId); if (entry != null) { WireParselet parselet = entry.getValue(); parselet.accept(entry.getKey(), wireIn.getValueIn()); return; } } // If methodId isn't found, use the field number parselet to parse. fieldNumberParselet.readOne(methodId, wireIn); } @NotNull @Override public VanillaWireParser register(@NotNull WireKey key, WireParselet valueInConsumer) { return register(key.name().toString(), key.code(), valueInConsumer); } /** * Registers a WireParselet with a keyName. This method calculates the hashcode * of the keyName and delegates to the private register method. * * @param keyName The name of the key to register. * @param valueInConsumer The WireParselet associated with the keyName. * @return Returns the current instance of VanillaWireParser for method chaining. */ @NotNull public VanillaWireParser register(String keyName, WireParselet valueInConsumer) { // Compute the hash code of the keyName and register. return register(keyName, keyName.hashCode(), valueInConsumer); } /** * Registers a WireParselet with a given keyName and code. * The keyName is stored in the namedConsumer map and the code * with its corresponding keyName in the numberedConsumer map. * * @param keyName The name of the key to register. * @param code The code associated with the keyName. * @param valueInConsumer The WireParselet associated with the keyName. * @return Returns the current instance of VanillaWireParser for method chaining. */ private VanillaWireParser register(String keyName, int code, WireParselet valueInConsumer) { // Store the WireParselet in the namedConsumer map using the keyName. namedConsumer.put(keyName, valueInConsumer); // Store the keyName and its WireParselet in the numberedConsumer map using the code. numberedConsumer.put(code, new AbstractMap.SimpleEntry<>(keyName, valueInConsumer)); return this; } @Override public WireParselet lookup(CharSequence name) { return namedConsumer.get(name); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy