First commit, added all projects

This commit is contained in:
2026-05-31 14:39:57 -04:00
commit d2930f7af5
247 changed files with 22376354 additions and 0 deletions
+7
View File
@@ -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>
+11
View File
@@ -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>
+23
View File
@@ -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>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

+66
View File
@@ -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);
}
}
+33
View File
@@ -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));
}
}
+57
View File
@@ -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);
}
}
+29
View File
@@ -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);
}
}
+182
View File
@@ -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()));
}
}
+126
View File
@@ -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());
}
}
+210
View File
@@ -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()));
}
}
+91
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff