下面列出了java.nio.MappedByteBuffer#position ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public StepExecutionResult execute(ExecutionContext context) throws IOException {
try (FileChannel channel =
FileChannel.open(
getFilesystem().resolve(getPath()),
StandardOpenOption.READ,
StandardOpenOption.WRITE)) {
MappedByteBuffer buffer = channel.map(READ_WRITE, 0, channel.size());
Elf elf = new Elf(buffer);
Preconditions.checkState(
elf.header.e_phoff == (int) elf.header.e_phoff,
"program headers are expected to be within 4GB of beginning of file");
buffer.position((int) elf.header.e_phoff);
for (int index = 0; index < elf.header.e_phnum * elf.header.e_phentsize; index++) {
buffer.put((byte) 0);
}
}
return StepExecutionResults.SUCCESS;
}
private void save()
{
Assert.assertTrue(max_data_size >= getMinAlloc());
Assert.assertNotEquals(0, data_loc);
Assert.assertNotNull(key);
Assert.assertNotNull(value);
int file = (int) (data_loc / SEGMENT_FILE_SIZE);
MappedByteBuffer mbb = getBufferMap(file);
int offset = (int) (data_loc % SEGMENT_FILE_SIZE);
synchronized(mbb)
{
mbb.position(offset);
mbb.putInt(max_data_size);
mbb.putShort((short)key.size());
mbb.put(key.toByteArray());
mbb.putInt(value.size());
mbb.put(value.toByteArray());
}
}
private void damageCommitlog(long offset) throws Exception {
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
File file = new File(messageStoreConfig.getStorePathCommitLog() + File.separator + "00000000000000000000");
FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024 * 1024 * 10);
int bodyLen = mappedByteBuffer.getInt((int) offset + 84);
int topicLenIndex = (int) offset + 84 + bodyLen + 4;
mappedByteBuffer.position(topicLenIndex);
mappedByteBuffer.putInt(0);
mappedByteBuffer.putInt(0);
mappedByteBuffer.putInt(0);
mappedByteBuffer.putInt(0);
mappedByteBuffer.force();
fileChannel.force(true);
fileChannel.close();
}
public void putBytes(long position, byte[] buff)
{
long t1 = System.nanoTime();
//Assert.assertTrue(position >= 0);
//Assert.assertTrue(position + buff.length <= total_size);
int to_write=buff.length;
int start_file = (int) (position / MAP_SIZE);
int start_offset = (int) (position % MAP_SIZE);
MappedByteBuffer map = map_list.get(start_file);
map.position(start_offset);
int len = Math.min(to_write, (int) (MAP_SIZE - start_offset));
map.put(buff, 0, len);
if (len < to_write)
{
map = map_list.get(start_file + 1);
map.position(0);
map.put(buff, len, to_write - len);
}
TimeRecord.record(t1, "long_map_put_bytes");
}
public synchronized void getBytes(long position, byte[] buff)
{
long t1 = System.nanoTime();
//Assert.assertTrue(position >= 0);
//Assert.assertTrue(position + buff.length <= total_size);
int to_read=buff.length;
int start_file = (int) (position / MAP_SIZE);
int start_offset = (int) (position % MAP_SIZE);
MappedByteBuffer map = map_list.get(start_file);
map.position(start_offset);
int len = Math.min(to_read, (int) (MAP_SIZE - start_offset));
map.get(buff, 0, len);
if (len < to_read)
{
map = map_list.get(start_file + 1);
map.position(0);
map.get(buff, len, to_read - len);
}
TimeRecord.record(t1, "long_map_get_bytes");
}
private static void resetSymbolAddressesInSymbolTable(
MappedByteBuffer machoBuffer, MachoSymTabCommand symTabCommand) {
machoBuffer.position(symTabCommand.getSymbolTableOffset());
for (int i = 0; i < symTabCommand.getNumberOfSymbolTableEntries(); ++i) {
// struct nlist_64 {
// union {
// uint32_t n_strx; // index into the string table
// } n_un;
// uint8_t n_type; // type flag, see below
// uint8_t n_sect; // section number or NO_SECT
// uint16_t n_desc; // see <mach-o/stab.h>
// uint64_t n_value; // value of this symbol (or stab offset)
// };
ObjectFileScrubbers.getLittleEndianInt(machoBuffer); // n_strx
machoBuffer.get(); // n_type
machoBuffer.get(); // n_sect
ObjectFileScrubbers.getLittleEndianShort(machoBuffer); // n_desc
ObjectFileScrubbers.putLittleEndianLong(machoBuffer, 0x0); // n_value
}
}
private synchronized void grow() {
try {
long start = this.units.size() * Unit.SIZE;
MappedByteBuffer mmf = this.ch.map(MapMode.READ_WRITE, start, Unit.SIZE * 100);
this.mmfs.add(mmf);
mmf.order(ByteOrder.nativeOrder());
for (int i=0; i<100; i++) {
Unit ii = new Unit();
mmf.position(i * Unit.SIZE);
mmf.limit(mmf.position() + Unit.SIZE);
ii.buf = mmf.slice();
ii.addr = UberUtil.getAddress(ii.buf);
this.units.add(ii);
}
}
catch (Exception ignored) {
// if it fails then nothing is logged
}
}
protected void writeInt(long position, int value)
{
int file = (int) (position / SEGMENT_FILE_SIZE);
int offset_in_file = (int) (position % SEGMENT_FILE_SIZE);
MappedByteBuffer mbb = getBufferMap(file);
synchronized(mbb)
{
mbb.position(offset_in_file);
mbb.putInt(value);
}
}
protected Map<String, Integer> loadTroughMap()
{
TreeMap<String,Integer> map = new TreeMap<>();
MappedByteBuffer mbb = getBufferMap(0);
synchronized(mbb)
{
for(int i=0; i<MAX_TROUGHS; i++)
{
mbb.position( (int)(LOCATION_TROUGH_TABLE_START + (8 + MAX_TROUGH_NAME_LEN) * i));
long ptr = mbb.getLong();
byte[] name = new byte[MAX_TROUGH_NAME_LEN];
mbb.get(name);
int len =0;
for(int j=0; (j<MAX_TROUGH_NAME_LEN) && (name[j] != 0); j++)
{
len++;
}
if (len > 0)
{
String name_str = new String(name, 0, len);
map.put(name_str, i);
}
else
{
map.put("__FREE", i);
}
}
}
return map;
}
protected void writeLong(long position, long value)
{
int file = (int) (position / SEGMENT_FILE_SIZE);
int offset_in_file = (int) (position % SEGMENT_FILE_SIZE);
MappedByteBuffer mbb = getBufferMap(file);
synchronized(mbb)
{
mbb.position(offset_in_file);
mbb.putLong(value);
}
}
public ByteBuffer getBuffer(DumpIndex idx) throws IOException {
if (cache.containsKey(idx)) {
MappedByteBuffer b = cache.get(idx);
b.position(0);
return b.duplicate().order(b.order());
}
FileChannel channel = dump.getChannel();
MappedByteBuffer res = channel.map(FileChannel.MapMode.READ_ONLY, idx.filePos, idx.size);
res.order(ByteOrder.LITTLE_ENDIAN);
cache.put(idx, res);
return res.duplicate().order(res.order());
}
private void flush(T entry) throws IOException {
Queue<T> inMemQueue = getInMemoryQueue();
int resultSize = sizeOf(entry);
maxResultSize = Math.max(maxResultSize, resultSize);
totalResultSize = hasMaxQueueSize ? maxResultSize * inMemQueue.size() : (totalResultSize + resultSize);
if (totalResultSize >= thresholdBytes) {
this.file = File.createTempFile(UUID.randomUUID().toString(), null);
RandomAccessFile af = new RandomAccessFile(file, "rw");
FileChannel fc = af.getChannel();
int writeIndex = 0;
mappingSize = Math.min(Math.max(maxResultSize, DEFAULT_MAPPING_SIZE), totalResultSize);
MappedByteBuffer writeBuffer = fc.map(MapMode.READ_WRITE, writeIndex, mappingSize);
int resSize = inMemQueue.size();
for (int i = 0; i < resSize; i++) {
T e = inMemQueue.poll();
writeToBuffer(writeBuffer, e);
// buffer close to exhausted, re-map.
if (mappingSize - writeBuffer.position() < maxResultSize) {
writeIndex += writeBuffer.position();
writeBuffer = fc.map(MapMode.READ_WRITE, writeIndex, mappingSize);
}
}
writeBuffer.putInt(EOF); // end
fc.force(true);
fc.close();
af.close();
flushedCount = resSize;
inMemQueue.clear();
flushBuffer = true;
}
}
private byte[] getBytes(
long offset, int nameLength, int cenExtLength, long size, boolean deflate) {
if (size > Integer.MAX_VALUE) {
throw new IllegalArgumentException("unsupported zip entry size: " + size);
}
try {
MappedByteBuffer fc =
chan.map(
MapMode.READ_ONLY,
offset,
Math.min(
LOCHDR + nameLength + cenExtLength + size + EXTRA_FIELD_SLACK,
chan.size() - offset));
fc.order(ByteOrder.LITTLE_ENDIAN);
checkSignature(path, fc, /* index= */ 0, 3, 4, "LOCSIG");
int locExtLength = fc.getChar(LOCEXT);
if (locExtLength > cenExtLength + EXTRA_FIELD_SLACK) {
// If the local header's extra fields don't match the central directory and we didn't
// leave enough slac, re-map the data section with the correct extra field length.
fc = chan.map(MapMode.READ_ONLY, offset + LOCHDR + nameLength + locExtLength, size);
fc.order(ByteOrder.LITTLE_ENDIAN);
} else {
// Otherwise seek past the local header, name, and extra fields to the data.
fc.position(LOCHDR + nameLength + locExtLength);
fc.limit((int) (LOCHDR + nameLength + locExtLength + size));
}
byte[] bytes = new byte[(int) size];
fc.get(bytes);
if (deflate) {
bytes =
ByteStreams.toByteArray(
new InflaterInputStream(
new ByteArrayInputStream(bytes), new Inflater(/*nowrap=*/ true)));
}
return bytes;
} catch (IOException e) {
throw new IOError(e);
}
}
private void open(boolean write) throws IOException {
if (this.raf != null) {
return;
}
// rename the existing file so we dont accidently lose previous crash scene
if (write && file.exists()) {
String filename = file.getName();
filename = System.currentTimeMillis() + "-" + filename;
File backup = new File(file.getParentFile(), filename);
file.renameTo(backup);
}
// open it
this.raf = new RandomAccessFile(file, write ? "rw" : "r");
this.ch = this.raf.getChannel();
// map units if this is read only
if (!write) {
int nbuffers = (int)(this.file.length() / Unit.SIZE);
MappedByteBuffer mmf = raf.getChannel().map(MapMode.READ_ONLY, 0, Unit.SIZE * nbuffers);
this.mmfs.add(mmf);
mmf.order(ByteOrder.nativeOrder());
for (int i=0; i<nbuffers; i++) {
Unit ii = new Unit();
mmf.position(i * Unit.SIZE);
mmf.limit(mmf.position() + Unit.SIZE);
ii.buf = mmf.slice();
ii.addr = UberUtil.getAddress(ii.buf);
this.units.add(ii);
}
this.ch.close();
this.raf.close();
}
}
public static long countOccurrencesV5(Path path, String text) throws IOException {
if (path == null || text == null) {
throw new IllegalArgumentException("Path/text cannot be null");
}
if (text.isBlank()) {
return 0;
}
final byte[] texttofind = text.getBytes(StandardCharsets.UTF_8);
long count = 0;
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
long position = 0;
long length = fileChannel.size();
while (position < length) {
long remaining = length - position;
long bytestomap = (long) Math.min(MAP_SIZE, remaining);
MappedByteBuffer mbBuffer = fileChannel.map(MapMode.READ_ONLY, position, bytestomap);
long limit = mbBuffer.limit();
long lastSpace = -1;
long firstChar = -1;
while (mbBuffer.hasRemaining()) {
boolean isFirstChar = false;
while (firstChar != 0 && mbBuffer.hasRemaining()) {
byte currentByte = mbBuffer.get();
if (Character.isWhitespace((char) currentByte)) {
lastSpace = mbBuffer.position();
}
if (texttofind[0] == currentByte) {
isFirstChar = true;
break;
}
}
if (isFirstChar) {
boolean isRestOfChars = true;
int j;
for (j = 1; j < texttofind.length; j++) {
if (!mbBuffer.hasRemaining() || texttofind[j] != mbBuffer.get()) {
isRestOfChars = false;
break;
}
}
if (isRestOfChars) {
count++;
lastSpace = -1;
}
firstChar = -1;
}
}
if (lastSpace > -1) {
position = position - (limit - lastSpace);
}
position += bytestomap;
}
}
return count;
}
/**
* Compute lenght of this sequence - quite expensive operation, indeed.
*/
@Override
public int length() {
if (length != -1) {
return length;
}
long start = System.currentTimeMillis();
int charactersRead = 0;
long bytesRead = 0;
MappedByteBuffer mappedByteBuffer = null;
CharBuffer charBuffer = CharBuffer.allocate(SIZE_LIMIT);
CharsetDecoder decoder = prepareDecoder(charset);
decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
try {
while (bytesRead < fileSize) {
mappedByteBuffer = fileChannel.map(
FileChannel.MapMode.READ_ONLY, bytesRead,
Math.min(SIZE_LIMIT, fileSize - bytesRead));
CoderResult result;
do {
charBuffer.clear();
result = decoder.decode(
mappedByteBuffer, charBuffer,
bytesRead + SIZE_LIMIT >= fileSize);
if (result.isUnmappable() || result.isMalformed()
|| result.isError()) {
throw new IOException("Error decoding file: "
+ result.toString() + " ");
}
if (bytesRead + SIZE_LIMIT >= fileSize) {
LOG.info("Coding end");
}
charactersRead += charBuffer.position();
} while (result.isOverflow());
int readNow = mappedByteBuffer.position();
bytesRead += readNow;
unmap(mappedByteBuffer);
}
charBuffer.clear();
boolean repeat;
do {
repeat = decoder.flush(charBuffer).isOverflow();
charactersRead += charBuffer.position();
charBuffer.clear();
} while (repeat);
} catch (IOException ex) {
if (mappedByteBuffer != null) {
unmap(mappedByteBuffer);
}
Exceptions.printStackTrace(ex);
}
length = charactersRead;
LOG.log(Level.INFO, "Length computed in {0} ms.", //NOI18N
System.currentTimeMillis() - start);
return length;
}
@Override
public StepExecutionResult execute(ExecutionContext context) throws IOException {
try (FileChannel channel =
FileChannel.open(
getFilesystem().resolve(getPath()),
StandardOpenOption.READ,
StandardOpenOption.WRITE)) {
MappedByteBuffer buffer = channel.map(READ_WRITE, 0, channel.size());
Elf elf = new Elf(buffer);
Elf.ElfSectionLookupResult sectionResult = elf.getMandatorySectionByName(getPath(), SECTION);
int sectionIndex = sectionResult.getIndex();
ElfSection section = sectionResult.getSection();
// Parse the dynamic section.
ElfDynamicSection dynamic = ElfDynamicSection.parse(elf.header.ei_class, section.body);
// Generate a new dynamic section with only the whitelisted tags.
ElfDynamicSection newDynamic =
new ElfDynamicSection(
RichStream.from(dynamic.entries)
.filter(e -> isKeepTag(e.d_tag) || !isRemoveScrubbedTags())
.map(e -> isKeepTag(e.d_tag) ? e : new ElfDynamicSection.Entry(e.d_tag, 0L))
.toImmutableList());
// Write out the new dynamic symbol table.
section.body.rewind();
newDynamic.write(elf.header.ei_class, section.body);
// Update the size in other parts of the ELF file, if necessary.
if (dynamic.entries.size() != newDynamic.entries.size()) {
Preconditions.checkState(isRemoveScrubbedTags());
// Update the section header.
buffer.position((int) (elf.header.e_shoff + sectionIndex * elf.header.e_shentsize));
section.header.withSize(section.body.position()).write(elf.header.ei_class, buffer);
// Update the `_DYNAMIC` symbol in the symbol table.
Optional<Elf.ElfSectionLookupResult> symtabSection = elf.getSectionByName(".symtab");
if (symtabSection.isPresent()) {
ElfSymbolTable symtab =
ElfSymbolTable.parse(elf.header.ei_class, symtabSection.get().getSection().body);
ElfSection strtab = elf.getMandatorySectionByName(getPath(), ".strtab").getSection();
ElfSymbolTable newSymtab =
new ElfSymbolTable(
RichStream.from(symtab.entries)
.map(
entry ->
strtab.lookupString(entry.st_name).equals("_DYNAMIC")
? entry.withSize(section.body.position())
: entry)
.toImmutableList());
// Write out the new symbol table.
symtabSection.get().getSection().body.rewind();
newSymtab.write(elf.header.ei_class, symtabSection.get().getSection().body);
}
}
}
return StepExecutionResults.SUCCESS;
}
public ZipIterable(Path path) throws IOException {
this.path = path;
this.chan = FileChannel.open(path, StandardOpenOption.READ);
// Locate the EOCD
long size = chan.size();
if (size < ENDHDR) {
throw new ZipException("invalid zip archive");
}
long eocdOffset = size - ENDHDR;
MappedByteBuffer eocd = chan.map(MapMode.READ_ONLY, eocdOffset, ENDHDR);
eocd.order(ByteOrder.LITTLE_ENDIAN);
int index = 0;
int commentSize = 0;
if (!isSignature(eocd, 0, 5, 6)) {
// The archive may contain a zip file comment; keep looking for the EOCD.
long start = Math.max(0, size - ENDHDR - 0xFFFF);
eocd = chan.map(MapMode.READ_ONLY, start, (size - start));
eocd.order(ByteOrder.LITTLE_ENDIAN);
index = (int) ((size - start) - ENDHDR);
while (index > 0) {
index--;
eocd.position(index);
if (isSignature(eocd, index, 5, 6)) {
commentSize = (int) ((size - start) - ENDHDR) - index;
eocdOffset = start + index;
break;
}
}
}
checkSignature(path, eocd, index, 5, 6, "ENDSIG");
int totalEntries = eocd.getChar(index + ENDTOT);
long cdsize = UnsignedInts.toLong(eocd.getInt(index + ENDSIZ));
int actualCommentSize = eocd.getChar(index + ENDCOM);
if (commentSize != actualCommentSize) {
throw new ZipException(
String.format(
"zip file comment length was %d, expected %d", commentSize, actualCommentSize));
}
// If the number of entries is 0xffff, check if the archive has a zip64 EOCD locator.
if (totalEntries == ZIP64_MAGICCOUNT) {
// Assume the zip64 EOCD has the usual size; we don't support zip64 extensible data sectors.
long zip64eocdOffset = size - ENDHDR - ZIP64_LOCHDR - ZIP64_ENDHDR;
MappedByteBuffer zip64eocd = chan.map(MapMode.READ_ONLY, zip64eocdOffset, ZIP64_ENDHDR);
zip64eocd.order(ByteOrder.LITTLE_ENDIAN);
// Note that zip reading is necessarily best-effort, since an archive could contain 0xFFFF
// entries and the last entry's data could contain a ZIP64_ENDSIG. Some implementations
// read the full EOCD records and compare them.
if (zip64eocd.getInt(0) == ZIP64_ENDSIG) {
cdsize = zip64eocd.getLong(ZIP64_ENDSIZ);
eocdOffset = zip64eocdOffset;
}
}
this.cd = chan.map(MapMode.READ_ONLY, eocdOffset - cdsize, cdsize);
cd.order(ByteOrder.LITTLE_ENDIAN);
}
@Override
public StepExecutionResult execute(ExecutionContext context) throws IOException {
try (FileChannel channel =
FileChannel.open(
getFilesystem().resolve(getPath()),
StandardOpenOption.READ,
StandardOpenOption.WRITE)) {
MappedByteBuffer buffer = channel.map(READ_WRITE, 0, channel.size());
Elf elf = new Elf(buffer);
ImmutableList<SectionUsingDynamicStrings> processors = getSectionProcesors(elf);
// Load the dynamic string table.
Elf.ElfSectionLookupResult dynStrSection = elf.getMandatorySectionByName(getPath(), DYNSTR);
byte[] dynStr = new byte[dynStrSection.getSection().body.remaining()];
dynStrSection.getSection().body.get(dynStr);
// Collect all the string references from the section processors.
ImmutableList<Long> stringIndices =
RichStream.from(processors)
.flatMap(p -> p.getStringReferences().stream())
.toImmutableList();
// Write the new dynamic string table out to a byte array and get the new string indices
// corresponding to the order of the collected string indices.
ByteArrayOutputStream newDynStrStream = new ByteArrayOutputStream();
ImmutableList<Integer> newStringIndices =
ElfStringTable.writeStringTableFromStringTable(
dynStr,
RichStream.from(stringIndices).map(i -> (int) (long) i).toImmutableList(),
newDynStrStream);
Preconditions.checkState(stringIndices.size() == newStringIndices.size());
byte[] newDynStr = newDynStrStream.toByteArray();
Preconditions.checkState(dynStrSection.getSection().header.sh_size >= newDynStr.length);
// Generate a map from old to new string indices which sections can use to update themselves.
Map<Long, Long> newStringIndexMapBuilder = new HashMap<>();
for (int i = 0; i < stringIndices.size(); i++) {
newStringIndexMapBuilder.put(stringIndices.get(i), (long) newStringIndices.get(i));
}
ImmutableMap<Long, Long> newStringIndexMap = ImmutableMap.copyOf(newStringIndexMapBuilder);
// Call back into the processors to update themselves with the new string indices.
processors.forEach(p -> p.processNewStringReferences(newDynStr.length, newStringIndexMap));
// Rewrite the dynamic string section.
dynStrSection.getSection().body.rewind();
dynStrSection.getSection().body.put(newDynStr);
// Fixup the version section header with the new size and write it out.
buffer.position(
(int) (elf.header.e_shoff + dynStrSection.getIndex() * elf.header.e_shentsize));
dynStrSection
.getSection()
.header
.withSize(dynStrSection.getSection().body.position())
.write(elf.header.ei_class, buffer);
}
return StepExecutionResults.SUCCESS;
}
public static void main(String[] args) throws Exception {
File file = new File(
"c:\\Program Files (x86)\\Bethesda.net Launcher\\games\\Fallout76\\Data\\SeventySix.esm");
Set<String> editorIds = new HashSet<>();
/*
* char[4] = 'EDID'
* uint16 = length
* uint8[length] = characters
*/
int i = 1;
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
MappedByteBuffer mbb = raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
while (mbb.remaining() > 0) {
if (mbb.get() == 'E') {
if (mbb.get() == 'D') {
if (mbb.get() == 'I') {
if (mbb.get() == 'D') {
int len = Short.reverseBytes(mbb.getShort()) & 0xFFFF;
byte[] data = new byte[len];
mbb.get(data);
editorIds.add(new String(data, StandardCharsets.ISO_8859_1));
if (i % 100 == 0) {
System.out.println("Records so far: " + i);
}
i++;
} else {
mbb.position(mbb.position() - 3);
}
} else {
mbb.position(mbb.position() - 2);
}
} else {
mbb.position(mbb.position() - 1);
}
}
}
}
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setTitle("Find Editor IDs");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(1000, 800);
frame.setLocationRelativeTo(null);
JTextField search = new JTextField();
frame.add(search, BorderLayout.PAGE_START);
Font f = new Font(Font.SANS_SERIF, Font.PLAIN, 22);
search.setFont(f);
JTextPane text = new JTextPane();
JScrollPane scroll = new JScrollPane(text);
frame.add(scroll, BorderLayout.CENTER);
f = new Font(Font.MONOSPACED, Font.PLAIN, 22);
text.setFont(f);
frame.setVisible(true);
search.addActionListener(e -> {
text.setText("");
String find = search.getText().trim().toLowerCase();
String[] ands = find.split("\\s+");
List<String> found = new ArrayList<>();
if (find.length() > 0) {
for (String s : editorIds) {
boolean has = true;
for (String a : ands) {
if (!s.toLowerCase().contains(a)) {
has = false;
break;
}
}
if (has) {
found.add(s);
}
}
if (found.size() == 0) {
text.setText("<not found>");
} else {
Collections.sort(found, String.CASE_INSENSITIVE_ORDER);
text.setText(String.join("\r\n", found));
text.setCaretPosition(0);
}
}
});
});
}