diff --git a/.project b/.project deleted file mode 100644 index d84eca6..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - AACStarter - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/README.md b/README.md new file mode 100644 index 0000000..84f689d --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Group Members + + - Rene Urias Jr. + + # Assignment Submission - Augmentive and Alternative Communication Devices + + This repository implements an Augmentative and Alternative Communication (AAC) system, allowing users to associate images with corresponding text to facilitate communication. It includes classes for managing categories, images, and their mappings. + + + ## Resources Used + +- **Official Java Documentation**: I extensively consulted the [Java documentation](https://docs.oracle.com/javase/8/docs/) to gain clarity on the usage of specific Java methods and classes. This was particularly helpful in understanding how to work with strings, characters, and arrays in Java. +- **StackOverflow**: In instances where I encountered challenges during coding, such as determining the length of user input or implementing certain mathematical aspects related to AAC devices, I sought solutions on [StackOverflow](https://stackoverflow.com/). This platform provided valuable examples and insights. +- **Java Style Guide**: To maintain consistent formatting and adhere to naming conventions, I followed the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). This practice contributes to improved code readability and maintainability. +- **Markdown Guide**: The structure and content of this README.md file were crafted using an [online Markdown guide](https://www.markdownguide.org/). This resource offered guidance on Markdown syntax, enabling effective documentation for the Augmentive and Alternative Communication Devices project. \ No newline at end of file diff --git a/src/AAC.java b/src/AAC.java index 2440faf..a59d35d 100644 --- a/src/AAC.java +++ b/src/AAC.java @@ -1,34 +1,32 @@ -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.ImageIcon; -import javax.swing.JButton; - import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; -import java.awt.GridLayout; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; - import java.util.Locale; import java.util.Scanner; - import javax.speech.Central; import javax.speech.synthesis.Synthesizer; import javax.speech.synthesis.SynthesizerModeDesc; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import structures.AACMappings; +import structures.KeyNotFoundException; /** - * Creates a GUI that has a grid of images that represent the - * communication device of the AAC. + * Creates a GUI that has a grid of images that represent the communication device of the AAC. * * @author Catie Baker * */ public class AAC implements ActionListener { - private JFrame frame; - //private JButton[][] grid; + private JFrame frame; + // private JButton[][] grid; private static Synthesizer synthesizer; private int startIndex; private int endIndex; @@ -41,25 +39,26 @@ public class AAC implements ActionListener { /** * Creates the AAC display for the file provided - * @param filename the name of the file that contains the - * images and text that will be in the AAC + * + * @param filename the name of the file that contains the images and text that will be in the AAC */ - public AAC(String filename){ + public AAC(String filename) { this.aacMappings = new AACMappings(filename); this.images = this.aacMappings.getImageLocs(); this.startIndex = 0; - this.endIndex = Math.min(NUM_ACROSS*NUM_DOWN, this.images.length); - frame=new JFrame(); - frame.setPreferredSize(new Dimension(500,500)); - loadImages(NUM_ACROSS,NUM_DOWN); + this.endIndex = Math.min(NUM_ACROSS * NUM_DOWN, this.images.length); + frame = new JFrame(); + frame.setPreferredSize(new Dimension(500, 500)); + loadImages(NUM_ACROSS, NUM_DOWN); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.pack(); + frame.pack(); frame.setVisible(true); input = new Scanner(System.in); } /** * Loads the images in the screen in a width by length grid + * * @param width the number of images across to display * @param length the number of images down to display */ @@ -67,9 +66,9 @@ public void loadImages(int width, int length) { Container pane = frame.getContentPane(); pane.removeAll(); - //add options to go to home screen + // add options to go to home screen JPanel panel1 = new JPanel(); - panel1.setLayout(new GridLayout(1,3)); + panel1.setLayout(new GridLayout(1, 3)); JButton home = new JButton(new ImageIcon("img/home.png")); home.setActionCommand(""); @@ -87,43 +86,43 @@ public void loadImages(int width, int length) { pane.add(panel1, BorderLayout.PAGE_START); - //if on page 2+, add back button - if(startIndex > 0) { + // if on page 2+, add back button + if (startIndex > 0) { JButton backArrow = new JButton(new ImageIcon("img/back-to.png")); backArrow.setActionCommand("back"); backArrow.addActionListener(this); pane.add(backArrow, BorderLayout.LINE_START); } - //add images + // add images JPanel panel = new JPanel(); - panel.setLayout(new GridLayout(width,length)); + panel.setLayout(new GridLayout(width, length)); int currImage = startIndex; - for(int y=0; y imageTextMapping; + + + // +--------------+------------------------------------------------ + // | Constructors | + // +--------------+ + /** + * Creates a new empty category with the given name. + * + * @param name The name of the category + */ + public AACCategory(String name) { + this.name = name; + this.imageTextMapping = new AssociativeArray<>(); + } // AACCategory(String) + + // +----------------+---------------------------------------------- + // | Public methods | + // +----------------+ + /** + * Adds the mapping of the imageLoc to the text to the category + * + * @param imageLoc The location of the image to add + * @param text The text that the image maps to + */ + public void addItem(String imageLoc, String text) { + imageTextMapping.set(imageLoc, text); + } // addItem(String, String) + + /** + * Returns the name of the category + * + * @return the name of the category + */ + public String getCategory() { + return name; + } // getCategory() + + /** + * Returns the text associated with the given image location in this category + * + * @param imageLoc The location of the image + * @return the text associated with the image + * @throws KeyNotFoundException when the image location is not found + */ + public String getText(String imageLoc) throws KeyNotFoundException { + return imageTextMapping.get(imageLoc); + } // getText(String) + + /** + * Determines if the provided image location if stored in the category + * + * @param imageLoc The location of the category + * @return true if it is in the category, false otherwise + */ + public boolean hasImage(String imageLoc) { + return imageTextMapping.hasKey(imageLoc); + } // hasImage(String) + + /** + * Returns an array of all the images in the category + * + * @return the array of image locations + */ + public String[] getImages() { + String[] images = new String[imageTextMapping.size()]; + int index = 0; + for (String key : imageTextMapping.getAllKeys()) { + images[index++] = key; + } + return images; + } // getImages() +} // class AACCategory diff --git a/src/structures/AACMappings.java b/src/structures/AACMappings.java new file mode 100644 index 0000000..239bba3 --- /dev/null +++ b/src/structures/AACMappings.java @@ -0,0 +1,149 @@ +package structures; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; + +/** + * AACMappings class represents the mappings for a set of Augmentative and Alterntive Communication + * (AAC) images and their associated texts. It manages categories, images, and their mappings, + * allowing for easy retrieval and manipulation of AAC data + * + * @author Rene Urias Jr. + * @version 1.0 of 15 October 2023 + */ +public class AACMappings { + // +--------+------------------------------------------------------ + // | Fields | + // +--------+ + private AACCategory defaultCategory; + private AACCategory currentCategory; + private AssociativeArray categories; + + // +--------------+------------------------------------------------ + // | Constructors | + // +--------------+ + /** + * Constructs an AACMappings object + * + * @param filename + */ + public AACMappings(String filename) { + // Initialzie the AACMappings with data from the file (to be implemented) + // For now, let's create a default category + defaultCategory = new AACCategory("default"); + currentCategory = defaultCategory; + categories = new AssociativeArray<>(); + categories.set("default", defaultCategory); + } // AACMappings(String) + + // +---------+----------------------------------------------------- + // | Methods | + // +---------+ + /** + * Adds a mapping to the current category (or the default category if that is the current + * category) + * + * @param imageLoc The location of the image + * @param text The text associated with the image + */ + public void add(String imageLoc, String text) { + currentCategory.addItem(imageLoc, text); + } // add(String, String) + + /** + * Provides an array of all the images in the current category + * + * @return an array of image locations + */ + public String[] getImageLocs() { + return currentCategory.getImages(); + } // getImageLocs() + + /** + * Given the image location selected, determines the associated text with the image. If the image + * provided is a category, it also updates the AAC's current category to be the category + * associated with that image. + * + * @param imageLoc The location where the image is stored + * @return the text associated with the current image + * @throws KeyNotFoundException + */ + public String getText(String imageLoc) throws KeyNotFoundException { + // Check if the image represents a category + if (isCategory(imageLoc)) { + // Update the current category to the category associated with the image + currentCategory = categories.get(imageLoc); + return "Category changed to: " + currentCategory.getCategory(); + } else { + // Retrieve the text associated with the image from the current category + try { + String text = currentCategory.getText(imageLoc); + return "Text for image " + imageLoc + ": " + text; + } catch (KeyNotFoundException e) { + return "Text not found for image " + imageLoc; + } + } + } + + /** + * Resets the current category of the AAC back to the default category + */ + public void reset() { + currentCategory = defaultCategory; + } // reset() + + /** + * Gets the current category + * + * @return the current category or the empty string if on the default category + */ + public String getCurrentCategory() { + return currentCategory.getCategory(); + } // getCurrentCategory() + + /** + * Determines if the image represents a category or text-to-speech. + * + * @param imageLoc The location where the image is stored + * @return true if the image represents a category, false if the image represents text-to-speech + */ + public boolean isCategory(String imageLoc) { + return currentCategory.hasImage(imageLoc); + } // isCategory(String) + + /** + * Writes the AAC mappings stored to a file. The file is formatted as the text location of the + * category followed by the text name of the category and then one line per item in the category + * that starts with > and then has the file name and text of that image. For instance: + * img/food/plate.png food >img/food/icons8-french-fries-96.png french fries + * >img/food/icons8-watermelon-96.png watermelon img/clothing/hanger.png clothing + * >img/clothing/collaredshirt.png collared shirt + * + * @param filename The name of the file to write the AAC mapping to. + */ + + public void writeToFile(String filename) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { + // Iterate through categories + for (String categoryName : categories.getAllKeys()) { + AACCategory category = categories.get(categoryName); + + // Write category information to the file + writer.write(categoryName + " " + category.getCategory()); + writer.newLine(); + + // Iterate through images in the category + for (String imageLoc : category.getImages()) { + String text = category.getText(imageLoc); + + // Write image information to the file + writer.write(">" + imageLoc + " " + text); + writer.newLine(); + } + } + } catch (IOException | KeyNotFoundException e) { + e.printStackTrace(); // Handle or log exceptions based on your application's needs + } + } +} // class AACMappings diff --git a/src/structures/AssociativeArray.java b/src/structures/AssociativeArray.java new file mode 100644 index 0000000..c1c9c5a --- /dev/null +++ b/src/structures/AssociativeArray.java @@ -0,0 +1,185 @@ +package structures; + +import java.lang.reflect.Array; + +/** + * A basic implementation of Associative Arrays with keys of type K and values of type V. + * Associative Arrays store key/value pairs and permit you to look up values by key. + * + * @author Rene Urias Jr. + * @author Samuel A. Rebelsky + */ +public class AssociativeArray { + // +-----------+--------------------------------------------------- + // | Constants | + // +-----------+ + + /** + * The default capacity of the initial array. + */ + static final int DEFAULT_CAPACITY = 16; + + // +--------+------------------------------------------------------ + // | Fields | + // +--------+ + + /** + * The size of the associative array (the number of key/value pairs). + */ + int size; + + /** + * The array of key/value pairs. + */ + KVPair pairs[]; + + // +--------------+------------------------------------------------ + // | Constructors | + // +--------------+ + + /** + * Create a new, empty associative array. + */ + @SuppressWarnings("unchecked") + public AssociativeArray() { + this.pairs = + (KVPair[]) Array.newInstance(new KVPair().getClass(), DEFAULT_CAPACITY); + this.size = 0; + } // AssociativeArray() + + // +------------------+-------------------------------------------- + // | Standard Methods | + // +------------------+ + + /** + * Create a copy of this AssociativeArray. + */ + public AssociativeArray clone() { + AssociativeArray clonedArray = new AssociativeArray<>(); + clonedArray.size = this.size; + clonedArray.pairs = java.util.Arrays.copyOf(this.pairs, this.size); + return clonedArray; + } // clone() + + /** + * Convert the array to a string. + */ + public String toString() { + StringBuilder result = new StringBuilder("{ "); + for (int i = 0; i < size; i++) { + result.append(pairs[i].key).append(": ").append(pairs[i].value); + if (i < size - 1) { + result.append(", "); + } + } + result.append(" }"); + return result.toString(); + } // toString() + + // +----------------+---------------------------------------------- + // | Public Methods | + // +----------------+ + + /** + * Set the value associated with key to value. Future calls to get(key) will return value. + */ + public void set(K key, V value) { + int index; + try { + index = find(key); + // Key found, replace the value + pairs[index].value = value; + } catch (KeyNotFoundException e) { + // Key not found, add a new key-value pair + if (size == pairs.length) { + expand(); + } + pairs[size++] = new KVPair<>(key, value); + } + } // set(K,V) + + /** + * Get the value associated with key. + * + * @throws KeyNotFoundException when the key does not appear in the associative array. + */ + public V get(K key) throws KeyNotFoundException { + int index = find(key); + return pairs[index].value; + } // get(K) + + /** + * Determine if key appears in the associative array. + */ + public boolean hasKey(K key) { + try { + find(key); + return true; + } catch (KeyNotFoundException e) { + return false; + } + } // hasKey(K) + + /** + * Remove the key/value pair associated with a key. Future calls to get(key) will throw an + * exception. If the key does not appear in the associative array, does nothing. + */ + public void remove(K key) { + try { + int index = find(key); + // Shift elements to the left to remove the key-value pair + System.arraycopy(pairs, index + 1, pairs, index, size - index - 1); + size--; + } catch (KeyNotFoundException e) { + // Key not found, do nothing + } + } // remove(K) + + /** + * Determine how many values are in the associative array. + */ + public int size() { + return this.size; + } // size() + + /** + * Get all keys in the associative array + * + * @return an array of all keys + */ + @SuppressWarnings("unchecked") + public K[] getAllKeys() { + Object keysObjectArray = + Array.newInstance(pairs.getClass().getComponentType().getComponentType(), size); + + for (int i = 0; i < size; i++) { + Array.set(keysObjectArray, i, pairs[i].key); + } + + return (K[]) keysObjectArray; + } // getAllKeys() + + // +-----------------+--------------------------------------------- + // | Private Methods | + // +-----------------+ + + /** + * Expand the underlying array. + */ + private void expand() { + pairs = java.util.Arrays.copyOf(pairs, this.pairs.length * 2); + } // expand() + + /** + * Find the index of the first entry in `pairs` that contains key. If no such entry is found, + * throws an exception. + */ + private int find(K key) throws KeyNotFoundException { + for (int i = 0; i < size; i++) { + if (pairs[i].key.equals(key)) { + return i; + } + } + throw new KeyNotFoundException(); + } // find(K) +} // class AssociativeArray diff --git a/src/structures/KVPair.java b/src/structures/KVPair.java new file mode 100644 index 0000000..3e567f2 --- /dev/null +++ b/src/structures/KVPair.java @@ -0,0 +1,52 @@ +package structures; + +/** + * An easy way to store key/value pairs. We assume that other classes will access fields directly. + */ +class KVPair { + // +--------+------------------------------------------------------ + // | Fields | + // +--------+ + + /** + * The key. + */ + K key; + + /** + * The value. + */ + V value; + + // +--------------+------------------------------------------------ + // | Constructors | + // +--------------+ + + /** + * Create an empty key/value pair. + */ + KVPair() { + this(null, null); + } // KVPair() + + /** + * Create a new key/value pair. + */ + KVPair(K key, V value) { + this.key = key; + this.value = value; + } // KVPair(K,V) + + // +------------------+-------------------------------------------- + // | Standard methods | + // +------------------+ + + public KVPair clone() { + return new KVPair(this.key, this.value); + } // clone() + + public String toString() { + return "{ " + this.key.toString() + " : " + this.value.toString() + " }"; + } // toString() +} // class KVPair + diff --git a/src/structures/KeyNotFoundException.java b/src/structures/KeyNotFoundException.java new file mode 100644 index 0000000..0e6d8e2 --- /dev/null +++ b/src/structures/KeyNotFoundException.java @@ -0,0 +1,26 @@ +package structures; + +/** + * Exceptions that indicate that a key is not in an associative array (dictionary, map, etc.). + * + * @author Samuel A. Rebelsky + */ +public class KeyNotFoundException extends Exception { + // +--------------+------------------------------------------------ + // | Constructors | + // +--------------+ + + /** + * Create a new exception. + */ + public KeyNotFoundException() { + super("key not found"); + } // KeyNotFoundException() + + /** + * Create a new exception with a particular message. + */ + public KeyNotFoundException(String message) { + super(message); + } // KeyNotFoundException(String) +} // KeyNotFoundException