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

main.java.com.amazon.ionpathextraction.FsmPathExtractor Maven / Gradle / Ivy

Go to download

Ion Path Extraction API aims to combine the convenience of a DOM API with the speed of a streaming API.

The newest version!
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at:
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file 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.amazon.ionpathextraction;

import static com.amazon.ionpathextraction.internal.Preconditions.checkArgument;
import static com.amazon.ionpathextraction.internal.Preconditions.checkState;

import com.amazon.ion.IonReader;
import com.amazon.ion.IonType;
import com.amazon.ionpathextraction.internal.PathExtractorConfig;
import java.util.List;
import java.util.function.BiFunction;

/**
 * A PathExtractor modeled as a Finite State Machine.
 * 
* Compared to the PathExtractorImpl, this supports a narrower set of * SearchPaths and their combinations, but is more performant, particularly * when a large number of field names are searched for. *
* A more comprehensive explanation of the strictness from a user PoV can be * found on the PathExtractorBuilder.buildStrict() API method. Notes on the * 'why' can be found in the FsmMatcherBuilder. */ class FsmPathExtractor implements PathExtractor { private final FsmMatcher rootMatcher; private final PathExtractorConfig config; private FsmPathExtractor( final FsmMatcher rootMatcher, final PathExtractorConfig config) { this.rootMatcher = rootMatcher; this.config = config; } static FsmPathExtractor create( final List> searchPaths, final PathExtractorConfig config) { FsmMatcherBuilder builder = new FsmMatcherBuilder<>( config.isMatchCaseInsensitive(), config.isMatchFieldsCaseInsensitive()); for (SearchPath path : searchPaths) { builder.accept(path); } return new FsmPathExtractor<>(builder.build(), config); } @Override public void match(final IonReader reader) { match(reader, null); } @Override public void match(final IonReader reader, final T context) { checkArgument(reader.getDepth() == 0 || config.isMatchRelativePaths(), "reader must be at depth zero, it was at: %s", reader.getDepth()); while (reader.next() != null) { matchCurrentValue(reader, context); } } @Override public void matchCurrentValue(final IonReader reader) { matchCurrentValue(reader, null); } @Override public void matchCurrentValue(final IonReader reader, final T context) { checkArgument(reader.getDepth() == 0 || config.isMatchRelativePaths(), "reader must be at depth zero, it was at: %s", reader.getDepth()); checkArgument(reader.getType() != null, "reader must be positioned at a value; call IonReader.next() first."); matchRecursive(reader, rootMatcher, context, -1, reader.getDepth()); } private int matchRecursive( final IonReader reader, final FsmMatcher matcher, final T context, final int position, final int initialDepth) { FsmMatcher child = matcher.transition(reader.getFieldName(), position, reader::getTypeAnnotations); if (child == null) { return 0; } if (child.callback != null) { int stepOut = invokeCallback(reader, child.callback, initialDepth, context); if (stepOut > 0) { return stepOut; } } if (IonType.isContainer(reader.getType()) && !child.terminal) { reader.stepIn(); int childPos = 0; while (reader.next() != null) { int stepOut = matchRecursive(reader, child, context, childPos++, initialDepth); if (stepOut > 0) { reader.stepOut(); return stepOut - 1; } } reader.stepOut(); } return 0; } private int invokeCallback( final IonReader reader, final BiFunction callback, final int initialReaderDepth, final T context) { int previousReaderDepth = reader.getDepth(); int stepOutTimes = callback.apply(reader, context); int newReaderDepth = reader.getDepth(); checkState(previousReaderDepth == newReaderDepth, "Reader must be at same depth when returning from callbacks. initial: %s, new: %s", previousReaderDepth, newReaderDepth); // we don't allow users to step out the initial reader depth int readerRelativeDepth = reader.getDepth() - initialReaderDepth; checkState(stepOutTimes <= readerRelativeDepth, STEP_OUT_TOO_FAR_MSG, stepOutTimes, readerRelativeDepth); return stepOutTimes; } private static final String STEP_OUT_TOO_FAR_MSG = "Callback return cannot be greater than the reader current relative depth. " + "return: %s, relative reader depth: %s"; }