First commit, added all projects
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
|
||||
<fileset name="all" enabled="true" check-config-name="CS480StyleGuide" local="false">
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>PA1</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,66 @@
|
||||
package app;
|
||||
|
||||
import gui.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import javax.imageio.*;
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
* An app that can be used to test the CompletingField
|
||||
* and CompletingDocument classes.
|
||||
*
|
||||
* @author Prof. David Bernstein, James Madison University
|
||||
* @version 1.0
|
||||
*/
|
||||
public class CompletingFieldApp implements Runnable
|
||||
{
|
||||
private String fn;
|
||||
|
||||
/**
|
||||
* Explicit Value Constructor.
|
||||
*
|
||||
* @param fn The name of the file to use
|
||||
*/
|
||||
public CompletingFieldApp(final String fn)
|
||||
{
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* The code to execute in the event dispatch thread.
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
JFrame frame = new JFrame("CompletingFieldApp");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
JPanel contentPane = (JPanel)frame.getContentPane();
|
||||
|
||||
contentPane.setLayout(new BorderLayout());
|
||||
contentPane.setBackground(Color.WHITE);
|
||||
|
||||
try
|
||||
{
|
||||
BufferedImage image = ImageIO.read(new File("logoWay.gif"));
|
||||
JLabel logo = new JLabel(new ImageIcon(image));
|
||||
contentPane.add(logo, BorderLayout.CENTER);
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
CompletingField field = new CompletingField();
|
||||
|
||||
field.setWordList(fn);
|
||||
field.setBorder(BorderFactory.createTitledBorder("Street Name"));
|
||||
contentPane.add(field, BorderLayout.SOUTH);
|
||||
|
||||
frame.setSize(300,300);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package app;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* A driver that can be used to test the CompletingField
|
||||
* and CompletingDocument classes.
|
||||
*
|
||||
* @author Prof. David Bernstein, James Madison University
|
||||
* @version 1.0
|
||||
*/
|
||||
public class CompletingFieldDriver
|
||||
{
|
||||
/**
|
||||
* The entry point of the application.
|
||||
*
|
||||
* @param args The command line arguments
|
||||
* @throws InterruptedException if something goes wrong
|
||||
* @throws InvocationTargetException if something goes wrong
|
||||
*/
|
||||
public static void main(final String[] args)
|
||||
throws InterruptedException, InvocationTargetException
|
||||
{
|
||||
String fn = "street-names.txt";
|
||||
if (args.length == 1)
|
||||
{
|
||||
fn = args[0];
|
||||
}
|
||||
|
||||
SwingUtilities.invokeAndWait(new CompletingFieldApp(fn));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package gui;
|
||||
|
||||
import text.WordFinder;
|
||||
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.PlainDocument;
|
||||
|
||||
/**
|
||||
* A document containing an auto-completing field.
|
||||
*/
|
||||
public class CompletingDocument extends PlainDocument implements Document
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private CompletingField field = null;
|
||||
private WordFinder finder = null;
|
||||
|
||||
/**
|
||||
* @param field The auto-completing field
|
||||
*/
|
||||
public CompletingDocument(final CompletingField field)
|
||||
{
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertString(final int offset, final String s, final AttributeSet as)
|
||||
throws BadLocationException
|
||||
{
|
||||
if (s == null || s.isEmpty())
|
||||
return;
|
||||
|
||||
super.insertString(offset, s, as);
|
||||
|
||||
if (this.finder == null) return;
|
||||
|
||||
String currentText = this.getText(0, getLength());
|
||||
String match = this.finder.find(currentText);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
String af = match.substring(currentText.length());
|
||||
super.insertString(getLength(), af, as);
|
||||
this.field.select(currentText.length(), match.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName The filename containing the list of words to auto-complete from
|
||||
*/
|
||||
public void setWordList(final String fileName)
|
||||
{
|
||||
this.finder = new WordFinder(fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package gui;
|
||||
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.text.Document;
|
||||
|
||||
|
||||
/**
|
||||
* An text field with the ability to set a list of words to attempt to auto-complete from.
|
||||
*/
|
||||
public class CompletingField extends JTextField
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private CompletingDocument document;
|
||||
|
||||
@Override
|
||||
protected Document createDefaultModel()
|
||||
{
|
||||
document = new CompletingDocument(this);
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName The name of a file to read a list of auto-complete candidates from
|
||||
*/
|
||||
public void setWordList(final String fileName)
|
||||
{
|
||||
document.setWordList(fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package testing;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.text.Document;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import gui.CompletingDocument;
|
||||
import gui.CompletingField;
|
||||
|
||||
class TestCompletingDocument
|
||||
{
|
||||
enum Prefix
|
||||
{
|
||||
CA("ca"),
|
||||
MA("ma"),
|
||||
C("c"),
|
||||
CARB("carb"),
|
||||
EMPTY("");
|
||||
|
||||
private final String value;
|
||||
|
||||
Prefix(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum Word
|
||||
{
|
||||
CAT("cat"),
|
||||
DOG("dog"),
|
||||
CAR("car"),
|
||||
CART("cart"),
|
||||
CARBON("carbon");
|
||||
|
||||
private final String value;
|
||||
|
||||
Word(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum FileLabel
|
||||
{
|
||||
WORDS("words.txt");
|
||||
|
||||
private final String value;
|
||||
|
||||
FileLabel(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@TempDir
|
||||
File tempDir;
|
||||
|
||||
private File writeWords(final List<Word> words) throws IOException
|
||||
{
|
||||
File file = new File(tempDir, FileLabel.WORDS.value());
|
||||
Files.write(
|
||||
file.toPath(),
|
||||
words.stream().map(Word::value).toList()
|
||||
);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Test
|
||||
void insertingTextWorksWhenFinderIsUnset() throws Exception
|
||||
{
|
||||
CompletingField field = new CompletingField();
|
||||
CompletingDocument doc = new CompletingDocument(field);
|
||||
field.setDocument(doc);
|
||||
|
||||
assertDoesNotThrow(() ->
|
||||
doc.insertString(0, Prefix.MA.value(), null)
|
||||
);
|
||||
|
||||
assertEquals(Prefix.MA.value(), doc.getText(0, doc.getLength()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullInsertIsIgnored() throws Exception
|
||||
{
|
||||
Document doc = new CompletingField().getDocument();
|
||||
|
||||
doc.insertString(0, null, null);
|
||||
|
||||
assertEquals(0, doc.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyInsertIsIgnored() throws Exception
|
||||
{
|
||||
Document doc = new CompletingField().getDocument();
|
||||
|
||||
doc.insertString(0, Prefix.EMPTY.value(), null);
|
||||
|
||||
assertEquals(0, doc.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
void noCompletionOccursWhenNoWordsMatch() throws Exception
|
||||
{
|
||||
File file = writeWords(List.of(Word.DOG));
|
||||
CompletingField field = new CompletingField();
|
||||
CompletingDocument doc = (CompletingDocument) field.getDocument();
|
||||
|
||||
field.setWordList(file.getAbsolutePath());
|
||||
doc.insertString(0, Prefix.CA.value(), null);
|
||||
|
||||
assertEquals(Prefix.CA.value(), doc.getText(0, doc.getLength()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void completionOccursForSingleMatch() throws Exception
|
||||
{
|
||||
File file = writeWords(List.of(Word.CAT));
|
||||
CompletingField field = new CompletingField();
|
||||
CompletingDocument doc = (CompletingDocument) field.getDocument();
|
||||
|
||||
field.setWordList(file.getAbsolutePath());
|
||||
doc.insertString(0, Prefix.C.value(), null);
|
||||
|
||||
assertEquals(Word.CAT.value(), doc.getText(0, doc.getLength()));
|
||||
assertEquals(1, field.getSelectionStart());
|
||||
assertEquals(3, field.getSelectionEnd());
|
||||
}
|
||||
|
||||
@Test
|
||||
void longestMatchingWordIsChosen() throws Exception
|
||||
{
|
||||
File file = writeWords(
|
||||
List.of(Word.CAR, Word.CART, Word.CARBON)
|
||||
);
|
||||
|
||||
CompletingField field = new CompletingField();
|
||||
CompletingDocument doc = (CompletingDocument) field.getDocument();
|
||||
|
||||
field.setWordList(file.getAbsolutePath());
|
||||
doc.insertString(0, Prefix.CARB.value(), null);
|
||||
|
||||
assertEquals(Word.CARBON.value(), doc.getText(0, doc.getLength()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void completionFallsBackToRawInsertWhenMatchIsNull() throws Exception
|
||||
{
|
||||
File file = writeWords(List.of(Word.DOG));
|
||||
CompletingField field = new CompletingField();
|
||||
CompletingDocument doc = (CompletingDocument) field.getDocument();
|
||||
|
||||
field.setWordList(file.getAbsolutePath());
|
||||
doc.insertString(0, Prefix.CA.value(), null);
|
||||
|
||||
assertEquals(Prefix.CA.value(), doc.getText(0, doc.getLength()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package testing;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.text.Document;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import gui.CompletingDocument;
|
||||
import gui.CompletingField;
|
||||
|
||||
class TestCompletingField
|
||||
{
|
||||
enum WordSource
|
||||
{
|
||||
STREETS("street-names.txt"),
|
||||
EMPTY_LIST("empty.txt");
|
||||
|
||||
private final String value;
|
||||
|
||||
WordSource(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum AssertionText
|
||||
{
|
||||
DOCUMENT_NULL("Document should not be null");
|
||||
|
||||
private final String value;
|
||||
|
||||
AssertionText(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletingField createFieldOnEdt() throws Exception
|
||||
{
|
||||
CompletingField[] holder = new CompletingField[1];
|
||||
|
||||
SwingUtilities.invokeAndWait(() ->
|
||||
{
|
||||
holder[0] = new CompletingField();
|
||||
});
|
||||
|
||||
return holder[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultConstructionUsesCompletingDocumentModel() throws Exception
|
||||
{
|
||||
CompletingField field = createFieldOnEdt();
|
||||
|
||||
Document document = field.getDocument();
|
||||
|
||||
assertNotNull(document, AssertionText.DOCUMENT_NULL.value());
|
||||
assertInstanceOf(CompletingDocument.class, document);
|
||||
}
|
||||
|
||||
@Test
|
||||
void settingWordSourceKeepsDocumentInitialized() throws Exception
|
||||
{
|
||||
CompletingField field = createFieldOnEdt();
|
||||
|
||||
SwingUtilities.invokeAndWait(() ->
|
||||
{
|
||||
field.setWordList(WordSource.STREETS.value());
|
||||
});
|
||||
|
||||
assertNotNull(field.getDocument());
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleWordListAssignmentsDoNotBreakDocument() throws Exception
|
||||
{
|
||||
CompletingField field = createFieldOnEdt();
|
||||
|
||||
SwingUtilities.invokeAndWait(() ->
|
||||
{
|
||||
field.setWordList(WordSource.STREETS.value());
|
||||
field.setWordList(WordSource.EMPTY_LIST.value());
|
||||
});
|
||||
|
||||
assertNotNull(field.getDocument());
|
||||
}
|
||||
|
||||
@Test
|
||||
void documentRemainsCompletingDocumentAfterWordListChange() throws Exception
|
||||
{
|
||||
CompletingField field = createFieldOnEdt();
|
||||
|
||||
SwingUtilities.invokeAndWait(() ->
|
||||
{
|
||||
field.setWordList(WordSource.STREETS.value());
|
||||
});
|
||||
|
||||
assertInstanceOf(CompletingDocument.class, field.getDocument());
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingSetWordListBeforeEdtCompletionDoesNotThrow() throws Exception
|
||||
{
|
||||
CompletingField[] holder = new CompletingField[1];
|
||||
|
||||
SwingUtilities.invokeAndWait(() ->
|
||||
{
|
||||
holder[0] = new CompletingField();
|
||||
holder[0].setWordList(WordSource.STREETS.value());
|
||||
});
|
||||
|
||||
assertNotNull(holder[0].getDocument());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package testing;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import text.WordFinder;
|
||||
|
||||
class TestWordFinder
|
||||
{
|
||||
enum FileName
|
||||
{
|
||||
DATA("data.txt"),
|
||||
MISSING("missing.txt"),
|
||||
SINGLE("single.txt"),
|
||||
ANIMALS("animals.txt"),
|
||||
WORDS("words.txt"),
|
||||
MIXED("mixed.txt");
|
||||
|
||||
private final String value;
|
||||
|
||||
FileName(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum Word
|
||||
{
|
||||
ANT("ant"),
|
||||
BEAR("bear"),
|
||||
TIGER("tiger"),
|
||||
|
||||
CAT("cat"),
|
||||
DOG("dog"),
|
||||
DEER("deer"),
|
||||
|
||||
CAMEL("camel"),
|
||||
CARROT("carrot"),
|
||||
CATNIP("catnip"),
|
||||
|
||||
|
||||
SUN("sun"),
|
||||
MOON("moon"),
|
||||
STARS("stars"),
|
||||
|
||||
ALPHA("alpha"),
|
||||
BETA("beta"),
|
||||
|
||||
NULL(null);
|
||||
|
||||
private final String value;
|
||||
|
||||
Word(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum Prefix
|
||||
{
|
||||
A("a"),
|
||||
CA("ca"),
|
||||
Z("z"),
|
||||
EMPTY(""),
|
||||
SPACE(" "),
|
||||
MULTI_SPACE(" ");
|
||||
|
||||
private final String value;
|
||||
|
||||
Prefix(final String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@TempDir
|
||||
File tempDir;
|
||||
|
||||
private File write(final FileName name, final List<Word> words) throws IOException
|
||||
{
|
||||
File file = new File(tempDir, name.value());
|
||||
Files.write(
|
||||
file.toPath(),
|
||||
words.stream().map(Word::value).toList()
|
||||
);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadsWordsAndSupportsPrefixSearch() throws Exception
|
||||
{
|
||||
File file = write(
|
||||
FileName.DATA,
|
||||
List.of(Word.TIGER, Word.ANT, Word.BEAR)
|
||||
);
|
||||
|
||||
WordFinder finder = new WordFinder(file.getAbsolutePath());
|
||||
|
||||
assertEquals(Word.ANT.value(), finder.find(Prefix.A.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingFileProducesNoResults() throws Exception
|
||||
{
|
||||
File missing = new File(tempDir, FileName.MISSING.value());
|
||||
WordFinder finder = new WordFinder(missing.getAbsolutePath());
|
||||
|
||||
assertNull(finder.find(Prefix.A.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullPrefixAlwaysReturnsNull() throws Exception
|
||||
{
|
||||
File file = write(
|
||||
FileName.SINGLE,
|
||||
List.of(Word.CAT)
|
||||
);
|
||||
|
||||
WordFinder finder = new WordFinder(file.getAbsolutePath());
|
||||
|
||||
assertNull(finder.find(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyOrWhitespacePrefixesReturnNull() throws Exception
|
||||
{
|
||||
File file = write(
|
||||
FileName.ANIMALS,
|
||||
List.of(Word.DOG, Word.DEER)
|
||||
);
|
||||
|
||||
WordFinder finder = new WordFinder(file.getAbsolutePath());
|
||||
|
||||
assertAll(
|
||||
() -> assertNull(finder.find(Prefix.EMPTY.value())),
|
||||
() -> assertNull(finder.find(Prefix.SPACE.value())),
|
||||
() -> assertNull(finder.find(Prefix.MULTI_SPACE.value()))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsAlphabeticallyFirstMatchingWord() throws Exception
|
||||
{
|
||||
File file = write(
|
||||
FileName.WORDS,
|
||||
List.of(Word.CARROT, Word.CATNIP, Word.CAMEL)
|
||||
);
|
||||
|
||||
WordFinder finder = new WordFinder(file.getAbsolutePath());
|
||||
|
||||
assertEquals(Word.CAMEL.value(), finder.find(Prefix.CA.value()));
|
||||
|
||||
File file2 = write(
|
||||
FileName.WORDS,
|
||||
List.of(Word.CARROT, Word.CATNIP, Word.CAMEL, Word.BEAR)
|
||||
);
|
||||
|
||||
WordFinder finder2 = new WordFinder(file2.getAbsolutePath());
|
||||
|
||||
assertEquals(Word.CAMEL.value(), finder2.find(Prefix.CA.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenNoWordsMatchPrefix() throws Exception
|
||||
{
|
||||
File file = write(
|
||||
FileName.WORDS,
|
||||
List.of(Word.SUN, Word.MOON, Word.STARS)
|
||||
);
|
||||
|
||||
WordFinder finder = new WordFinder(file.getAbsolutePath());
|
||||
|
||||
assertNull(finder.find(Prefix.Z.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoresNullEntriesInWordFile() throws Exception
|
||||
{
|
||||
File file = write(
|
||||
FileName.MIXED,
|
||||
List.of(Word.ALPHA, Word.NULL, Word.BETA)
|
||||
);
|
||||
|
||||
WordFinder finder = new WordFinder(file.getAbsolutePath());
|
||||
|
||||
assertEquals(Word.ALPHA.value(), finder.find(Prefix.A.value()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package text;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A utility to find the first word in an alphabetically-sorted list that starts with an input
|
||||
* prefix.
|
||||
*/
|
||||
public class WordFinder
|
||||
{
|
||||
private List<String> words = null;
|
||||
|
||||
/**
|
||||
* @param fileName The file to read the list of words from
|
||||
*/
|
||||
public WordFinder(final String fileName)
|
||||
{
|
||||
Path filePath = Paths.get(fileName);
|
||||
try
|
||||
{
|
||||
this.words = Files.readAllLines(filePath).stream().map(s -> s.trim())
|
||||
.collect(Collectors.toList());
|
||||
Collections.sort(this.words);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prefix The prefix to match against
|
||||
* @return The first word matching the prefix or null if no matches were found
|
||||
*/
|
||||
public String find(final String prefix)
|
||||
{
|
||||
if (prefix == null || prefix.isBlank() || this.words == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int low = 0;
|
||||
int high = this.words.size() - 1;
|
||||
String lprefix = prefix.toLowerCase();
|
||||
|
||||
int validPrefixIndex = -1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
int mid = low + (high - low) / 2;
|
||||
|
||||
String midWord = this.words.get(mid);
|
||||
if (midWord.toLowerCase().startsWith(lprefix))
|
||||
{
|
||||
validPrefixIndex = mid;
|
||||
break;
|
||||
}
|
||||
|
||||
int comp = midWord.compareToIgnoreCase(lprefix);
|
||||
|
||||
if (comp < 0)
|
||||
{
|
||||
low = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (validPrefixIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
while (validPrefixIndex > 0
|
||||
&& this.words.get(validPrefixIndex - 1).toLowerCase().startsWith(lprefix))
|
||||
{
|
||||
validPrefixIndex--;
|
||||
}
|
||||
|
||||
return this.words.get(validPrefixIndex);
|
||||
}
|
||||
}
|
||||
+62895
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user