/*
* The MIT License
*
* Copyright 2014 Ryan Gilera.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.daytron.flipit.map.creator.model;
import com.github.daytron.flipit.map.creator.MainApp;
import com.github.daytron.flipit.map.creator.utility.GlobalSettings;
import com.github.daytron.flipit.map.creator.utility.StringUtils;
import com.github.daytron.simpledialogfx.data.DialogResponse;
import com.github.daytron.simpledialogfx.data.DialogText;
import com.github.daytron.simpledialogfx.data.DialogType;
import com.github.daytron.simpledialogfx.dialog.Dialog;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.WritableImage;
import javax.imageio.ImageIO;
/**
* A static class for map file management.
*
* @author Ryan Gilera ryangilera@gmail.com
*/
public final class MapManager {
private MapManager() {
}
/**
* Saves the map object as a JSON file.
*
* @param file The map file to save.
*/
public static boolean saveFile(File file, Map map,
Canvas canvas) {
LogManager logManager = LogManager.getInstance();
String userOS = GlobalSettings.USER_OS;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
// convert java object to JSON format,
// and returned as JSON formatted string
String json = gson.toJson(map);
try {
//write converted json data to a file
FileWriter writer = new FileWriter(file);
writer.write(json);
writer.close();
// Add new log
String successSaveMsg = GlobalSettings.LOG_SAVE_MAP
+ "Map is successfully saved.";
logManager.addNewLogMessage(successSaveMsg);
// Prepares for taking snapshot of the canvas
WritableImage writableImage = new WritableImage(
(int) canvas.getWidth(),
(int) canvas.getHeight());
canvas.snapshot(null, writableImage);
// Get the last filepath to use for saving image file
String filePath;
if (userOS.startsWith(GlobalSettings.OS_LINUX)
|| userOS.startsWith(GlobalSettings.OS_MAC)) {
filePath = file.getPath().substring(0, file.getPath().lastIndexOf("/") + 1);
} else {
// Other OS is already filtered above, so this is definitely Windows
filePath = file.getPath().substring(0, file.getPath().lastIndexOf("\\") + 1);
}
// Build full path
String imageFilePath = filePath + map.getMapID() + ".png";
// Create file
File fileImage = new File(imageFilePath);
// Try saving it as a PNG image file
try {
ImageIO.write(SwingFXUtils.fromFXImage(writableImage, null),
"png", fileImage);
} catch (Exception s) {
Dialog exceptionDialog = new Dialog(s);
exceptionDialog.showAndWait();
return false;
}
} catch (IOException e) {
// Add new log
String errorMsg = GlobalSettings.LOG_ERROR
+ "IOEXCEPTION! Unable to save file.";
logManager.addNewLogMessage(errorMsg);
// Show exception dialog
Dialog exceptionDialog = new Dialog(e);
exceptionDialog.showAndWait();
e.printStackTrace();
return false;
}
return true;
}
/**
* Opens the JSON map file
*
* @param file The file object that holds the map data
* @param isCurrentMapSave A boolean flag that tracks if the current map
* generated is saved or not
* @param app The MainApp object for retrieving the stage object
* @return the Map object if successfully opened, otherwise
* <code>null</code>
*/
public static Map openFile(File file, boolean isCurrentMapSave) {
LogManager logManager = LogManager.getInstance();
// Confirmation dialog
if (!isCurrentMapSave) {
Dialog confirmDialog = new Dialog(
DialogType.CONFIRMATION,
DialogText.CONFIRMATION_HEADER.getText(),
GlobalSettings.DIALOG_NEW_MAP_HEAD_MSG_NOT_SAVE,
GlobalSettings.DIALOG_NEW_MAP_BODY_MSG_NOT_SAVE);
confirmDialog.showAndWait();
if (confirmDialog.getResponse() == DialogResponse.NO
|| confirmDialog.getResponse() == DialogResponse.CLOSE) {
// Cancel opening file if user press cancel or press close (x)
return null;
}
}
Gson gson = new Gson();
BufferedReader br;
try {
br = new BufferedReader(
new FileReader(file));
//convert the json string back to object
Map map = gson.fromJson(br, Map.class);
return map;
} catch (FileNotFoundException ex) {
Logger.getLogger(MapManager.class.getName()).log(Level.SEVERE, null, ex);
String errorMsg = GlobalSettings.LOG_ERROR
+ "IOEXCEPTION! Error loading map (invalid json map file). ";
logManager.addNewLogMessage(errorMsg);
Dialog exceptionDialog = new Dialog(ex);
exceptionDialog.showAndWait();
return null;
}
}
/**
* Verifies if the File object is a valid map file for opening the file.
*
* @param file The file object that holds the map data
* @param app The MainApp object for retrieving the stage object
* @return <code>true</code> if the file is valid, otherwise
* <code>false</code>
*/
public static boolean verifyFileToOpen(File file) {
LogManager logManager = LogManager.getInstance();
String userOS = GlobalSettings.USER_OS;
// Possible action if user press cancel button on open file dialog
// Skip all remaining lines
if (file == null) {
return false;
}
// Extract extension name
String ext = file.getPath().substring(file.getPath().lastIndexOf(".") + 1);
// Extract filename
String filename = "";
if (userOS.startsWith(GlobalSettings.OS_LINUX)
|| userOS.startsWith(GlobalSettings.OS_MAC)) {
filename = file.getPath().substring(file.getPath().lastIndexOf("/") + 1);
} else if (userOS.startsWith(GlobalSettings.OS_WINDOWS)) {
filename = file.getPath().substring(file.getPath().lastIndexOf("\\") + 1);
} else {
String noOSsupportMsg = GlobalSettings.LOG_ERROR
+ GlobalSettings.LOG_OS_NOT_SUPPORTED_BODY_MSG;
logManager.addNewLogMessage(noOSsupportMsg);
return false;
}
// Get the first 3 letters for inspection
String firstWord = filename.substring(0, 3);
if (file.isFile()) {
if ("json".equals(ext)) {
if (firstWord.equals("Map")) {
return true;
} else {
String msgHead = "Invalid Filename";
String msgBody = "Map files must "
+ "start at \"Map\" followed by a number";
String invalidPatterName = GlobalSettings.LOG_ERROR
+ msgHead + ". " + msgBody;
logManager.addNewLogMessage(invalidPatterName);
Dialog errorDialog = new Dialog(
DialogType.ERROR, msgHead, msgBody);
errorDialog.showAndWait();
return false;
}
} else {
String msgHead = "Invalid Extension";
String msgBody = "Map files are "
+ ".json files";
String invalidExtension = GlobalSettings.LOG_ERROR
+ msgHead + ". " + msgBody;
logManager.addNewLogMessage(invalidExtension);
Dialog errorDialog = new Dialog(
DialogType.ERROR, msgHead, msgBody);
errorDialog.showAndWait();
return false;
}
} else {
String msgHead = "No File Detected";
String msgBody = "Not a proper file.";
String invalidFile = GlobalSettings.LOG_ERROR
+ msgHead + ". " + msgBody;
logManager.addNewLogMessage(invalidFile);
Dialog errorDialog = new Dialog(
DialogType.ERROR, msgHead, msgBody);
errorDialog.showAndWait();
return false;
}
}
/**
*
* @param file The file object that holds the map data
* @param logManager The LogManager that handles log events
* @param app The MainApp object for retrieving the stage object
* @param map The Map object that holds the map data
* @param numberOfColumns The number of columns
* @param numberOfRows The number of rows
* @return <code>true</code> if the file is valid, otherwise
* <code>false</code>
*/
public static boolean verifyFileToSave(File file,
LogManager logManager, Map map, int numberOfColumns,
int numberOfRows) {
String userOS = GlobalSettings.USER_OS;
// Possible action if user press cancel button on save file dialog
// Skip all remaining lines
if (file == null) {
return false;
}
String ext = file.getPath().substring(file.getPath().lastIndexOf(".") + 1);
String filename = "";
if (userOS.startsWith(GlobalSettings.OS_LINUX)
|| userOS.startsWith(GlobalSettings.OS_MAC)) {
filename = file.getPath().substring(file.getPath().lastIndexOf("/") + 1);
} else if (userOS.startsWith(GlobalSettings.OS_WINDOWS)) {
filename = file.getPath().substring(file.getPath().lastIndexOf("\\") + 1);
} else {
String noOSsupportMsg = GlobalSettings.LOG_ERROR
+ GlobalSettings.LOG_OS_NOT_SUPPORTED_BODY_MSG;
logManager.addNewLogMessage(noOSsupportMsg);
Dialog errorDialog = new Dialog(
DialogType.ERROR,
GlobalSettings.LOG_OS_NOT_SUPPORTED_HEAD_MSG,
GlobalSettings.LOG_OS_NOT_SUPPORTED_BODY_MSG);
errorDialog.showAndWait();
return false;
}
String firstWord = filename.substring(0, filename.lastIndexOf("."));
// Prevents user from using a space character on Title
if (filename.contains(" ")) {
// Add new log
String wrongFormatNameMsg = GlobalSettings.LOG_WARNING
+ "Filename shouldn't contain space character";
logManager.addNewLogMessage(wrongFormatNameMsg);
// Show a warning dialog
Dialog warningDialog = new Dialog(
DialogType.WARNING,
GlobalSettings.DIALOG_SAVE_NAME_SPACE_HEAD_MSG,
GlobalSettings.DIALOG_SAVE_NAME_SPACE_BODY_MSG);
warningDialog.showAndWait();
return false;
}
if (!isValidFileName(firstWord)) {
String msgHead = "Invalid Filename Format";
String msgBody = "Should be [\"Map\"] + [3 digit number].json."
+ "Example: Map004.json";
String wrongFormatNameMsg = GlobalSettings.LOG_WARNING
+ msgHead + ". " + msgBody;
logManager.addNewLogMessage(wrongFormatNameMsg);
Dialog errorDialog = new Dialog(
DialogType.ERROR,
msgHead,
msgBody);
errorDialog.showAndWait();
return false;
}
if ("json".equalsIgnoreCase(ext)) {
// Formay the mapId to proper accepted format
// E.g. Map002
firstWord = formatMapID(firstWord);
// Apply data to map file
map.setMapID(firstWord);
map.setSize(new int[]{numberOfColumns, numberOfRows});
return true;
} else {
// Update log
String invalidExtMsg = GlobalSettings.LOG_ERROR
+ "Invalid extension file!";
logManager.addNewLogMessage(invalidExtMsg);
// Show error dialog
Dialog errorDialog = new Dialog(
DialogType.ERROR,
GlobalSettings.DIALOG_INVALID_EXTENSION_HEAD_MSG,
GlobalSettings.DIALOG_INVALID_EXTENSION_BODY_MSG);
errorDialog.showAndWait();
return false;
}
}
/**
* Verifies if the filename follows the name convention: "Map" + [3 digit
* number].
*
* @param filename The filename to inspect.
* @return <code>true</code> if the filename is valid, otherwise
* <code>false</code>.
*/
private static boolean isValidFileName(String filename) {
if (!filename.startsWith("Map")) {
return false;
}
if (filename.length() != 6) {
return false;
}
if (!(Character.isDigit(filename.charAt(3))
|| Character.isDigit(filename.charAt(4))
|| Character.isDigit(filename.charAt(5)))) {
return false;
}
return true;
}
/**
* Formats the map's ID for capital first letter and lowercase on preceding
* characters.
*
* @param id The ID to be formatted.
* @return Returns the formatted ID.
*/
private static String formatMapID(String id) {
id = id.toLowerCase();
return StringUtils.capitalizeFirstLetterWord(id);
}
}