diff --git a/.gitignore b/.gitignore index 259148f..80fd22f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,15 @@ *.exe *.out *.app +out/* + +# CMake +CMakeLists.txt +cmake-build-debug + +# Jetbrains IDE files +.idea/* + +# Input and Output folders +input/* +output/* \ No newline at end of file diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..a3fc409 --- /dev/null +++ b/src/Main.java @@ -0,0 +1,528 @@ +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; +import java.nio.file.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Scanner; +import java.util.stream.Stream; + +public class Main { + + //System independent path to output folder + public static String outputPath = System.getProperty("user.dir") + File.separator + "output" + File.separator; + //System independent path to input folder + public static String inputPath = System.getProperty("user.dir") + File.separator + "input" + File.separator; + + public static void main(String[] args) { + System.out.println("Welcome to GoIStreamToolRedux CLI!\ntype 'help' for more info. Type 'verify' if first start.\nAwaiting User input..."); + boolean live = true; + Scanner scanner = new Scanner(System.in); + while (live) { + System.out.print("> "); + String input = scanner.nextLine(); + CLIParse(input); + } + } + + private static void CLIParse(String input) { + Scanner scanner = new Scanner(System.in); //stdin + + switch (input) { + case "quit": case "exit": case "q": + System.out.println("exiting..."); + System.exit(0); + break; + case "help": + System.out.println("TOURNAMENT NUMBER"); + System.out.println(" 'number set', update current tourney number"); + +// System.out.println(" TIMER"); +// System.out.println("'timer add', add time to timer"); +// System.out.println("'timer set', set timer length"); +// System.out.println("'timer start', start timer"); +// System.out.println("'timer cancel', end timer"); + + System.out.println("TEAMS"); + System.out.println(" 'team add', add a team"); + System.out.println(" 'team add-noimg', add a team (no image)"); +// System.out.println("'team remove', remove a team"); + System.out.println(" 'team set', set an active team (MAIN FCN)"); + + System.out.println("MAPS"); + System.out.println(" 'map set', set current map"); + + break; + case "team set": case "ts": + System.out.print("Enter team's full name: "); + String teamName = scanner.nextLine(); + System.out.print("\nEnter team's letter: "); + String teamLetter = scanner.nextLine(); + try { + if (setTeam(teamName,teamLetter)) { + System.out.println("Updated!"); + } else { + System.out.println("Failed to Update Team Completely. Risk of Incomplete update! " + + "Check the previous error message for details"); + } + } catch (IOException exception) { + exception.printStackTrace(); + } catch (IllegalArgumentException exception) { + System.err.println(exception.getMessage()); + } + break; + + case "map set": case "ms": + System.out.print("Enter full map name: "); + String mapName = scanner.nextLine(); + try { + if (setMap(mapName)) { + System.out.println("Current Map Updated!"); + } else { + System.err.println("Failed to Update Current Map"); + } + } catch (NoSuchFileException exception) { + System.err.println("NoSuchFileException! Missing map png? Details: " + exception.getMessage()); + } catch (IOException exception) { + System.err.println("I/O Error! reason: " + exception.getMessage()); + } + break; + + case "number set": + System.out.print("Enter tournament number: "); + String tourneyNumber = scanner.nextLine(); + if (setTourneyNumber(tourneyNumber)) { + System.out.println("Current tournament number updated!"); + } else { + System.err.println("Failed to update current tournament number."); + } + break; + + case "verify": case "v": + if (verifyFolders()) { + System.out.println("All folders verified!"); + } else System.err.println("Folder recreation failed!"); + break; + + case "team add": + System.out.println("Enter Team Name: "); + String fullName0 = scanner.nextLine(); + System.out.println("Enter team abbreviation: "); + String shortName0 = scanner.nextLine(); + System.out.println("Enter image path: "); + Path imagePath = Paths.get(scanner.nextLine()); + try { + addTeam(fullName0, shortName0, imagePath); + } catch (IOException exception) { + exception.printStackTrace(); + } + break; + + case "team add-noimg": + System.out.println("Enter Team Name: "); + String fullName = scanner.nextLine(); + System.out.println("Enter team abbreviation: "); + String shortName = scanner.nextLine(); + try { + addTeam(fullName, shortName); + } catch (IOException exception) { + exception.printStackTrace(); + } + break; + + default: + System.out.println("Unknown Input. Type 'help' for commands"); + } + } + + /** + * Sets the current tournament number. + * + * @param number new number representing current tournament + * @return false if IO operation failed + */ + private static boolean setTourneyNumber(String number) { + //write 'number' to TournamentNumber.txt + try { + Writer fileWriter = new FileWriter(outputPath + "TournamentNumber.txt"); + fileWriter.write(number); + fileWriter.close(); + System.out.println("TournamentNumber.txt Updated"); + } catch (IOException e) { + System.err.println("Couldn't write to TournamentNumber.txt, reason: " + e.getMessage()); + return false; + } + + return true; + } + + /** + * Checks for input and output folders, creates them if necessary. + * + * @return true if folders found or recreation successful; false otherwise. + */ + private static boolean verifyFolders() { + System.out.println("verifying folders"); + boolean status = true; + + //check input folder + Path inputPathObject = Paths.get(inputPath); + if (!Files.exists(inputPathObject)) { + System.out.println("Input folder cannot be found... recreating"); + if (inputPathObject.toFile().mkdirs()) { + System.out.println("Input folder created successfully"); + } else { + System.err.println("Error when creating input directory!"); + status = false; + } + } else System.out.println("Input folder found"); + + //check output folder + Path outputPathObject = Paths.get(outputPath); + if (!Files.exists(outputPathObject)) { + System.out.println("Output folder cannot be found... recreating"); + if (outputPathObject.toFile().mkdirs()) { + System.out.println("Output folder created successfully"); + } else { + System.err.println("Error when creating output directory!"); + status = false; + } + } else System.out.println("Output folder found"); + + //check map_images folder + Path map_imagesPathObject = Paths.get(inputPath + "map_images"); + if (!Files.exists(outputPathObject)) { + System.out.println("map_images folder cannot be found... recreating"); + if (outputPathObject.toFile().mkdirs()) { + System.out.println("map_images folder created successfully"); + } else { + System.err.println("Error when creating map_images directory!"); + status = false; + } + } else System.out.println("map_images folder found"); + + //check team_logos folder + Path team_logosPathObject = Paths.get(inputPath + "team_logos"); + if (!Files.exists(outputPathObject)) { + System.out.println("team_logos folder cannot be found... recreating"); + if (outputPathObject.toFile().mkdirs()) { + System.out.println("team_logos folder created successfully"); + } else { + System.err.println("Error when creating team_logos directory!"); + status = false; + } + } else System.out.println("team_logos folder found"); + + return status; + } + + /** + * Checks if a map name is a valid map found in maps.txt. + * + * @param map Full name of a map to check + * @return true if map was found in maps.txt; false otherwise. + */ + private static boolean isValidMap(String map) throws FileNotFoundException { + //open Maps.txt + File teamsTXT = new File(inputPath + "maps.txt"); + Scanner scanner = new Scanner(teamsTXT); + //parse through File for 'map' + while (scanner.hasNext()) { + String line = scanner.nextLine(); + if (line.equalsIgnoreCase(map)) { + return true; + } + } + + return false; + } + + /** + * Sets the current map. + * + * @param mapName Name of map to be set as current. + * @return false if map could not be updated (Doesnt exist, IO error). True otherwise. + * @throws IOException + */ + private static boolean setMap(String mapName) throws IOException { + + /*--- Check Map Validity ---*/ + try { + if (!isValidMap(mapName)) { + System.err.println("That map does not exist in maps.txt"); + return false; + } + } catch (FileNotFoundException e) { + System.err.println("Map file could not be found, reason:" + e.getMessage()); + return false; + } + + /*--- Update Map.png ---*/ + //copy map from map_images + Files.copy(Paths.get(inputPath + "map_images" + File.separator + mapName + ".png"), Paths.get(outputPath + "Map.png"), StandardCopyOption.REPLACE_EXISTING); + System.out.println("Map.png Updated"); + + /*--- Update Map.txt ---*/ + Writer fileWriter = new FileWriter(outputPath + "Map.txt"); + fileWriter.write(mapName); + fileWriter.close(); + System.out.println("Map.txt Updated"); + + return true; + } + + /** + * Sets the current team. + * E.g., used when setting "The Skyborne" as team A. + * + * Updates the output teamA/B image with the new team's image. + * File is in the format `"Team" + teamIdentifier + ".png"` + * Updates the team & teamShort txt files to the new team's name. + * File name formatted as `"Team" + teamIdentifier + ".txt"`, + * and `"TeamShort" + teamIdentifier + ".txt"` + * + * @param newTeamName Name of the team. Must occur in teams.txt + * @param teamIdentifier Value to identify team. Must be a valid character for filesystem + * @return success or fail + * @throws IOException error writing, reading, or converting files. + * @throws IllegalArgumentException Thrown if fcn args contain invalid characters or do not correspond to a team in teams.txt + */ + static boolean setTeam(String newTeamName, String teamIdentifier) throws IOException, IllegalArgumentException { + + //throw exception if teamIdentifier contains filesystem illegal characters + final String[] ILLEGAL_CHARACTERS = { "/", "\n", "\r", "\t", "\0", "\f", "`", "?", "*", "\\", "<", ">", "|", "\"", ":", ".", ".." }; + if (Arrays.stream(ILLEGAL_CHARACTERS).anyMatch(teamIdentifier::contains)) { + throw new IllegalArgumentException("Team identifier contains illegal characters"); + } + //throw exception if team name has the separator character "|" used by teams.txt + if (newTeamName.contains("|")) { + throw new IllegalArgumentException("Team name contains illegal character, '|'"); + } + + teamIdentifier = teamIdentifier.toUpperCase(); + System.out.println("setTeam("+newTeamName+","+teamIdentifier+")"); + + /*--- Update TXTs ---*/ + + //get file names + String teamTXT = "Team" + teamIdentifier + ".txt"; + String teamShortTXT = "TeamShort" + teamIdentifier + ".txt"; + + //update output file "TeamShortX.txt" + Writer fileWriter2 = new FileWriter(outputPath + teamShortTXT); //TeamShortX.txt + String shortname = getShortName(newTeamName); + if (shortname == null) { + throw new IllegalArgumentException("Team '" + newTeamName + "' does not exist in teams.txt"); + } + fileWriter2.write(shortname); //write shortName to file + fileWriter2.close(); + System.out.println(teamShortTXT + " updated."); + + //update output file "TeamX.txt" + Writer fileWriter = new FileWriter(outputPath + teamTXT); //TeamX.txt + fileWriter.write(newTeamName); + fileWriter.close(); + System.out.println(teamTXT + " updated."); + + + + /*--- Update Image ---*/ + + //get all files in team_logos folder + LinkedList allTeamImages = new LinkedList<>(); + try (Stream paths = Files.walk(Paths.get(inputPath + "team_logos"))) { + paths + .filter(Files::isRegularFile) + .forEach(allTeamImages::add); + } + //find specific team's logo from list + Path teamLogo = findFile(allTeamImages, newTeamName); + if (teamLogo == null) { + System.err.println("team logo not found!"); + //remove current output team logo to prevent mismatch + // better to have right text and no logo, than right text and wrong logo. + File outputTeamLogo = new File(outputPath + "Team" + teamIdentifier + ".png"); + outputTeamLogo.delete(); + return false; + } + System.out.println("Found Team logo: " + teamLogo); + + //If extension is .png move it, else convert then move. + String logoExt = teamLogo.getFileName().toString().substring(teamLogo.getFileName().toString().indexOf(".") + 1); + if (logoExt.equalsIgnoreCase("png")) { + + //copy from teamLogo Path to TeamX.png + Files.copy(teamLogo,Paths.get(outputPath + "Team" + teamIdentifier + ".png"), StandardCopyOption.REPLACE_EXISTING); + } else { //logo not PNG + try { + //convert to png + boolean wasConverted = convertFormat(teamLogo.toString(),inputPath + "team_logos" + File.separator + newTeamName + ".png","PNG"); + if (wasConverted) { + System.out.println("Image converted to PNG"); + + //delete old photo + if(teamLogo.toFile().delete()) { + System.out.println("File deleted successfully"); + } + else { + System.err.println("Failed to delete the file"); + return false; + } + + //copy from teamLogo Path to TeamX.png + Files.copy(Paths.get(inputPath + "team_logos" + File.separator + newTeamName + ".png"),Paths.get(outputPath + "Team" + teamIdentifier + ".png"), StandardCopyOption.REPLACE_EXISTING); + + } else { + System.err.println("Image could not be converted to PNG"); + //remove current output team logo to prevent mismatch + // better to have right text and no logo, than right text and wrong logo. + File outputTeamLogo = new File(outputPath + "Team" + teamIdentifier + ".png"); + outputTeamLogo.delete(); + } + } catch (IOException exception) { + System.err.println("Error during converting image."); + exception.printStackTrace(); + return false; + } + } + + //successful finish + return true; + } + + /** + * Adds a team with no logo + * Updates teams.txt. + * @param fullName + * @param shortName + */ + static void addTeam(String fullName, String shortName) throws IOException { + //create string + String newTeamLine = System.lineSeparator() + fullName + "|" + shortName; + + //add string to teams.txt + Files.write(Paths.get(inputPath + "teams.txt"), newTeamLine.getBytes(), StandardOpenOption.APPEND); + } + + static void addTeam(String fullName, String shortName, Path image) throws IOException { + //add team to txt + addTeam(fullName, shortName); + + //parse image filetype + String file = image.getFileName().toString(); + String ext = file.substring(file.indexOf(".")); //fixme will error if path is not a file/doesnt have an extension + + + //add image + // copy from image Path to input/team_logos/fullName.ext + Files.copy(image, Paths.get(inputPath + "team_logos" + File.separator + fullName + ext)); + } + + /** + * Adds a team with a logo + * Updates teams.txt and the corresponding image + * + * {@// TODO: 6/17/21 https://stackoverflow.com/a/41069565/10714709 need to modify request} + * @param fullName + * @param shortName + * @param url + */ + static void addTeam(String fullName, String shortName, URL url) throws IOException { +// //add team to txt +// addTeam(fullName, shortName); +// +// //parse url extension/filetype +// String file = url.getFile(); +// String extension = file.substring(file.indexOf(".")); //includes "." +// +// //download image to file +// try(InputStream in = url.openStream()){ +// Files.copy(in, Paths.get(outputPath + fullName + extension)); +// } + } + + /** + * {@// FIXME: 6/17/21 implement fcn} + * Adds a team with no logo + * Updates teams.txt. + * @param fullName + * @param shortName + * @param logo + */ + static void addTeam(String fullName, String shortName, File logo) throws IOException { +// //add team to txt +// addTeam(fullName, shortName); +// +// //add image +// + } + + /** + * Takes a list of {@link Path}s and finds a certain file by name, excluding extension + * @param allTeamImages + * @return Path of found file. + */ + private static Path findFile(LinkedList allTeamImages, String searchFor) { + for (Path aPath : allTeamImages) { + if (aPath.getFileName().toString().substring(0,aPath.getFileName().toString().indexOf(".")).equalsIgnoreCase(searchFor)) { + return aPath; + } + } + return null; + } + + /** + * Parses `teams.txt` to get a team's longName from its shortName. + * Case insensitive search. Uppercase return. + * Can be used to verify the existence of a team by their full team name. + * + * @param longName longName to find corresponding shortName of + * @return Shortname, or null if no shortname can be found + * @throws FileNotFoundException + */ + public static String getShortName(String longName) throws FileNotFoundException { + //open input/teams.txt + File teamsTXT = new File(inputPath + "teams.txt"); + Scanner scanner = new Scanner(teamsTXT); + //parse through File for longName + while (scanner.hasNext()) { + String line = scanner.nextLine().toLowerCase(); + if (line.contains(longName.toLowerCase())) { + //long name line has been found, get shortname. + return line.substring(line.indexOf("|") + 1).toUpperCase(); + } + } + return null; + } + + /** + * Converts an image to another format + * @author Nam Ha Minh + * + * @param inputImagePath Path of the source image + * @param outputImagePath Path of the destination image + * @param formatName the format to be converted to, one of: jpeg, png, + * bmp, wbmp, and gif + * @return true if successful, false otherwise + * @throws IOException if errors occur during writing + */ + public static boolean convertFormat(String inputImagePath, + String outputImagePath, String formatName) throws IOException { + FileInputStream inputStream = new FileInputStream(inputImagePath); + FileOutputStream outputStream = new FileOutputStream(outputImagePath); + + // reads input image from file + BufferedImage inputImage = ImageIO.read(inputStream); + + // writes to the output image in specified format + boolean result = ImageIO.write(inputImage, formatName, outputStream); + + // needs to close the streams + outputStream.close(); + inputStream.close(); + + + return result; + } +} \ No newline at end of file