com.github.parboiled1.grappa.backport.buffers.CharSequenceInputBuffer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of grappa-tracer-backport Show documentation
Show all versions of grappa-tracer-backport Show documentation
A backport of the trace parser for grappa 1.0.x/parboiled1
/*
* Copyright (C) 2014 Francis Galiegue
*
* 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.github.parboiled1.grappa.backport.buffers;
import com.google.common.base.Preconditions;
import com.google.common.collect.Range;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.parboiled.support.Chars;
import org.parboiled.support.IndexRange;
import org.parboiled.support.Position;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import java.nio.CharBuffer;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* An {@link InputBuffer} over a {@link CharSequence}
*
* A {@link CharSequence} is the most basic character interface in the JDK.
* It is implemented by a lot of character related classes, including {@link
* String}, and is also the argument type used by a {@link Pattern}'s {@link
* Matcher}.
*
* Among other things, this means you can use this package on very large
* files using largetext, which
* implements {@link CharSequence} over multi-gigabyte files.
*
* This is a backport from grappa 2.0.x.
*/
@Immutable
public final class CharSequenceInputBuffer
implements InputBuffer
{
private static final ExecutorService EXECUTOR_SERVICE;
static {
final ThreadFactory factory = new ThreadFactoryBuilder()
.setDaemon(true).setNameFormat("linecounter-thread-%d").build();
EXECUTOR_SERVICE = Executors.newCachedThreadPool(factory);
}
private final CharSequence charSequence;
private final Future lineCounter;
public static InputBuffer fromLegacy(
final org.parboiled.buffers.InputBuffer legacyBuffer
)
{
return new CharSequenceInputBuffer(loadLegacyBuffer(legacyBuffer));
}
public CharSequenceInputBuffer(@Nonnull final CharSequence charSequence)
{
this.charSequence = Objects.requireNonNull(charSequence);
lineCounter = EXECUTOR_SERVICE.submit(new Callable()
{
@Override
public LineCounter call()
throws Exception
{
return new LineCounter(charSequence);
}
});
}
public CharSequenceInputBuffer(@Nonnull final char[] chars)
{
this(CharBuffer.wrap(Objects.requireNonNull(chars)));
}
@Override
public char charAt(final int index)
{
if (index < 0)
throw new IllegalArgumentException("index is negative");
return index < charSequence.length() ? charSequence.charAt(index)
: Chars.EOI;
}
@SuppressWarnings("ImplicitNumericConversion")
@Override
public int codePointAt(final int index)
{
final int length = charSequence.length();
if (index >= length)
return -1;
if (index < 0)
throw new IllegalArgumentException("index is negative");
final char c = charSequence.charAt(index);
if (!Character.isHighSurrogate(c))
return c;
if (index == length - 1)
return c;
final char c2 = charSequence.charAt(index + 1);
return Character.isLowSurrogate(c2) ? Character.toCodePoint(c, c2) : c;
}
@Override
public boolean test(final int index, final char[] characters)
{
final int length = characters.length;
if (index + length > charSequence.length())
return false;
for (int i = 0; i < length; i++)
if (charSequence.charAt(index + i) != characters[i])
return false;
return true;
}
@Override
public String extract(final int start, final int end)
{
final int realStart = Math.max(start, 0);
final int realEnd = Math.min(end, charSequence.length());
return charSequence.subSequence(realStart, realEnd).toString();
}
@Override
public String extract(final IndexRange range)
{
return extract(range.start, range.end);
}
@Override
public Position getPosition(final int index)
{
return Futures.getUnchecked(lineCounter).toPosition(index);
}
@Override
public String extractLine(final int lineNumber)
{
Preconditions.checkArgument(lineNumber > 0, "line number is negative");
final LineCounter counter = Futures.getUnchecked(lineCounter);
final Range range = counter.getLineRange(lineNumber);
final int start = range.lowerEndpoint();
int end = range.upperEndpoint();
if (charAt(end - 1) == '\n')
end--;
if (charAt(end - 1) == '\r')
end--;
return extract(start, end);
}
@SuppressWarnings("AutoUnboxing")
@Override
public IndexRange getLineRange(final int lineNumber)
{
final Range range
= Futures.getUnchecked(lineCounter).getLineRange(lineNumber);
return new IndexRange(range.lowerEndpoint(), range.upperEndpoint());
}
@Override
public int getLineCount()
{
return Futures.getUnchecked(lineCounter).getNrLines();
}
@Override
public int length()
{
return charSequence.length();
}
private static CharSequence loadLegacyBuffer(
final org.parboiled.buffers.InputBuffer buffer)
{
final int bufsize = 4096;
String s;
final StringBuilder sb = new StringBuilder();
int len;
int index = 0;
do {
s = buffer.extract(index, index + bufsize);
len = s.length();
sb.append(s);
index += bufsize;
} while (len == bufsize);
return sb;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy