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

com.groupbyinc.common.jackson.jq.internal.functions._SubImplFunction Maven / Gradle / Ivy

There is a newer version: 198
Show newest version
package net.thisptr.jackson.jq.internal.functions;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

import org.joni.Matcher;
import org.joni.Option;
import org.joni.Region;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;

import net.thisptr.jackson.jq.Function;
import net.thisptr.jackson.jq.JsonQuery;
import net.thisptr.jackson.jq.Scope;
import net.thisptr.jackson.jq.exception.JsonQueryException;
import net.thisptr.jackson.jq.internal.BuiltinFunction;
import net.thisptr.jackson.jq.internal.misc.OnigUtils;
import net.thisptr.jackson.jq.internal.misc.Preconditions;

@BuiltinFunction("_sub_impl/3")
public class _SubImplFunction implements Function {
	@Override
	public List apply(final Scope scope, final List args, final JsonNode in) throws JsonQueryException {
		Preconditions.checkInputType("_sub_impl/3", in, JsonNodeType.STRING);

		final List result = new ArrayList<>();

		for (final JsonNode regexText : args.get(0).apply(scope, in)) {
			Preconditions.checkArgumentType("_sub_impl/3", 1, regexText, JsonNodeType.STRING);

			for (final JsonNode flagsText : args.get(2).apply(scope, in)) {
				Preconditions.checkArgumentType("_sub_impl/3", 3, flagsText, JsonNodeType.STRING);

				final OnigUtils.Pattern p = new OnigUtils.Pattern(regexText.asText(), flagsText.asText());
				concat(result, sub(scope, p, in.asText(), args.get(1)));
			}
		}

		return result;
	}

	private static void concat(final List out, final int index, final Stack stack, final List> values) {
		if (index == values.size()) {
			final StringBuilder builder = new StringBuilder();
			for (final JsonNode item : stack) {
				if (item.isTextual()) {
					builder.append(item.asText());
				} else {
					builder.append(item.toString());
				}
			}
			out.add(TextNode.valueOf(builder.toString()));
		} else {
			for (final JsonNode value : values.get(index)) {
				stack.push(value);
				concat(out, index + 1, stack, values);
				stack.pop();
			}
		}
	}

	private static void concat(final List out, final List> values) {
		concat(out, 0, new Stack<>(), values);
	}

	private static List> sub(final Scope scope, final OnigUtils.Pattern pattern, final String inputText, final JsonQuery replace) throws JsonQueryException {
		final List> result = new ArrayList<>();

		final byte[] inputBytes = inputText.getBytes(StandardCharsets.UTF_8);
		final Matcher m = pattern.regex.matcher(inputBytes);
		int offset = 0;
		do {
			if (m.search(offset, inputBytes.length, Option.NONE) < 0)
				break;

			result.add(Collections.singletonList(TextNode.valueOf(new String(inputBytes, offset, m.getBegin() - offset, StandardCharsets.UTF_8))));

			final ObjectNode captures = scope.getObjectMapper().createObjectNode();
			final Region regions = m.getRegion();
			if (regions != null) {
				for (int i = 1; i < regions.numRegs; ++i) {
					final String name = pattern.names[i];
					if (name == null)
						continue;
					if (regions.beg[i] >= 0) {
						final String value = new String(inputBytes, regions.beg[i], regions.end[i] - regions.beg[i], StandardCharsets.UTF_8);
						captures.set(name, TextNode.valueOf(value));
					} else {
						captures.set(name, NullNode.getInstance());
					}
				}
			}

			result.add(replace.apply(scope, captures));

			offset = m.getEnd();
		} while (pattern.global && offset != inputBytes.length);

		result.add(Collections.singletonList(TextNode.valueOf(new String(inputBytes, offset, inputBytes.length - offset, StandardCharsets.UTF_8))));
		return result;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy