How to use H2 databases with a custom file extension? - java

I'm building an application which stores its data in H2 database files, so my save files have the extension .h2.db.
When opening the application, the user has to select which save file should be loaded. To make it easier to recognize these files, is it possible to tell H2 to use custom file extensions?
Looking at the Database URL overview of H2, I can only specify the name of the database. I would prefer a extension like .save over the default .h2.db. Is there a reasonable way to achieve this?
A workaround would be to link the *.save-file to a temporary folder, renaming it to the correct suffix. If this is the only solution, I guess I would go with the default extension.

H2 database supports pluggable file system so with a bit of extra code you can use any extension you want. You just need to create a wrapper, register it and use your own database URL. The wrapper could look like this:
package my.test;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathWrapper;
public class FilePathTestWrapper extends FilePathWrapper {
private static final String[][] MAPPING = {
{".h2.db", ".save"},
{".lock.db", ".save.lock"}
};
#Override
public String getScheme() {
return "save";
}
#Override
public FilePathWrapper wrap(FilePath base) {
// base.toString() returns base.name
FilePathTestWrapper wrapper = (FilePathTestWrapper) super.wrap(base);
wrapper.name = getPrefix() + wrapExtension(base.toString());
return wrapper;
}
#Override
protected FilePath unwrap(String path) {
String newName = path.substring(getScheme().length() + 1);
newName = unwrapExtension(newName);
return FilePath.get(newName);
}
protected static String wrapExtension(String fileName) {
for (String[] pair : MAPPING) {
if (fileName.endsWith(pair[1])) {
fileName = fileName.substring(0, fileName.length() - pair[1].length()) + pair[0];
break;
}
}
return fileName;
}
protected static String unwrapExtension(String fileName) {
for (String[] pair : MAPPING) {
if (fileName.endsWith(pair[0])) {
fileName = fileName.substring(0, fileName.length() - pair[0].length()) + pair[1];
break;
}
}
return fileName;
}
}
Then you'll need to register it:
FilePathTestWrapper wrapper = new FilePathTestWrapper();
FilePath.register(wrapper);
And use database URL like this:
"jdbc:h2:save:./mydatabase"
Note the "save:" prefix, it should match string returned by getScheme() method. I put a bit more details here: http://shuvikov.net/blog/renaming-h2-database-files

You cannot define a new extension (suffix) for the H2 database name (page file)
In my code i have a similar need (user may select the database) and this is what i do:
File file=new FileImport(shell, "*"+org.h2.engine.Constants.SUFFIX_PAGE_FILE).filePicker();
if (file!=null){
String database=file.getAbsolutePath().
replace(org.h2.engine.Constants.SUFFIX_PAGE_FILE, "");
...
}
NOTE: FileImport is a class writen by me that extends SWT FileDialog: https://code.google.com/p/marcolopes/source/browse/org.dma.eclipse/src/org/dma/eclipse/swt/dialogs/file/FileImport.java

The filename extensions (notice there are many, not just .h2.db) are set in Constants.java:
/**
* The file name suffix of page files.
*/
public static final String SUFFIX_PAGE_FILE = ".h2.db";
And used directly throughout the codebase, e.g. in Database.java:
/**
* Check if a database with the given name exists.
*
* #param name the name of the database (including path)
* #return true if one exists
*/
static boolean exists(String name) {
if (FileUtils.exists(name + Constants.SUFFIX_PAGE_FILE)) {
return true;
}
return FileUtils.exists(name + Constants.SUFFIX_MV_FILE);
}
So no, you cannot specify a custom file extension for the database. You'll be much better off either not worrying about this, or using a FileNameExtensionFilter to limit your user's choices, like JoopEggen suggested.
You could zip up your database files and rename the .zip to some other extension like .save. Then you unzip your .save file in order to get to the .h2.db file. Note this is how Jars work - they're just zip files under the covers.
You might also look at the Game Developer Stack Exchange, and consider posting a question there like "How do I create user-friendly save files" and describe your problem if you really can't expect your users to tolerate .h2.db files.

Related

Set the text of a Bukkit config file by default (PaperMc 1.15)

I created a Locale setting so Italian, English, ... I needed to know how to set up a predefined config already: I have obviously tried how every good person does this but I think it is too inefficient, I also tried to create files through the IDE in the same location where the files in the DataFolder are created at the onEnable but obviously it didn't work, however what I tried to be ineffective is this: customConfig.set("Hi-Message", "I'm sorry, i love you");
The way I'm doing it right now, is simply having the config file in the source code itself and, if the file-version of the config does not exist yet, creates a new config file using the config file from the compiled source code.
In my onEnable method in Main, I simply call a method in another class FileManager.setup().
It looks a bit like this in setup():
public static void setup() throws IOException {
File plugin_work_directory = new File(plugin_work_path);
core_server_config = new File(plugin_work_path + "config.txt");
if (!plugin_work_directory.exists()) plugin_work_directory.mkdir();
if (!core_server_config.exists()) {
InputStream core_server_config_template = (Main.class.getResourceAsStream("/config.txt"));
Files.copy(core_server_config_template, Paths.get(plugin_work_path + "config.txt"));
}
Config.load();
if (Integer.parseInt(Config.getValue("config.version")) < Config.version) {
core_server_config.delete();
InputStream core_server_config_template = (Main.class.getResourceAsStream("/config.txt"));
Files.copy(core_server_config_template, Paths.get(plugin_work_path + "config.txt"));
}
Config.load();
}
Config.load() parses the values into a private hashmap of the Config class, whereby other classes can reference the hashmap through a String getValue(String string) method.

Java:Get Extension of file with "." in its name

Question might confuse you but read below for clarifications...
I am making a simple console project to get a extension of a file by inputting name from user.
I am using solution described here:
How do I get the file extension of a file in Java?
It solve my 90% problem
I have split file name on the basis of "." and "/".
But it will not run for input "a.out" or some otjer examples like this
it will give extension as ".out" actually being extension less
So is there any solution for thia case????
please help me
Sorry for my english
I assume you are looking for getting the extension of file without the .(Dot) and return empty string if there is no extension. The following code may provide the desired outcome.
public static void main(String[] args){
System.out.println(retrieveFileExtension("input.txt"));
}
private static String retrieveFileExtension(String fileName) {
try {
return fileName.substring(fileName.lastIndexOf(".") + 1);
} catch (Exception e) {
return "";
}
}

Save Excel file with OLE, Java on Eclipse RCP Application

I try to Save an Excel file. The Excel file is a template with makros (*.xltm). I can open the file and edit the content, but if i try to save the destination Excel file is corrupt.
I try to save the file with:
int id = _workbook.getIDsOfNames(new String[] {"Save"})[0];
_workbook.invoke(id);
or/and
_xlsClientSite.save(_file, true);
You might try specifying a file format in your Save call.
If you're lucky, you can find the file format code you need in the Excel help. If you can't find what you need there, you'll have to get your hands dirty using the OLEVIEW.EXE program. There's likely a copy of it sitting on your hard drive somewhere, but if not, it's easy enough to find a copy with a quick Google search.
To use OLEVIEW.EXE:
Run it
Crack open the 'Type Libraries' entry
Find the version of Excel that you're using
Open that item
Search the enormous pile of text that's displayed for the string 'XlFileFormat'
Examine the XLFileFormat enum for a code that seems promising
If you are using Office2007 ("Excel12") like I am, you might try one of these values:
xlOpenXMLWorkbookMacroEnabled = 52
xlOpenXMLTemplateMacroEnabled = 53
Here's a method that I use to save Excel files using OLE:
/**
* Save the given workbook in the specified format.
*
* #param controlSiteAuto the OLE control site to use
* #param filepath the file to save to
* #param formatCode XlFileFormat code representing the file format to save as
* #param replaceExistingWithoutPrompt true to replace an existing file quietly, false to ask the user first
*/
public void saveWorkbook(OleAutomation controlSiteAuto, String filepath, Integer formatCode, boolean replaceExistingWithoutPrompt) {
Variant[] args = null;
Variant result = null;
try {
// suppress "replace existing?" prompt, if necessary
if (replaceExistingWithoutPrompt) {
setPropertyOnObject(controlSiteAuto, "Application", "DisplayAlerts", "False");
}
// if the given formatCode is null, for some reason, use a reasonable default
if (formatCode == null) {
formatCode = 51; // xlWorkbookDefault=51
}
// save the workbook
int[] id = controlSiteAuto.getIDsOfNames(new String[] {"SaveAs", "FileName", "FileFormat"});
args = new Variant[2];
args[0] = new Variant(filepath);
args[1] = new Variant(formatCode);
result = controlSiteAuto.invoke(id[0], args);
if (result == null || !result.getBoolean()) {
throw new RuntimeException("Unable to save active workbook");
}
// enable alerts again, if necessary
if (replaceExistingWithoutPrompt) {
setPropertyOnObject(controlSiteAuto, "Application", "DisplayAlerts", "True");
}
} finally {
cleanup(args);
cleanup(result);
}
}
protected void cleanup(Variant[] variants) {
if (variants != null) {
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].dispose();
}
}
}
}

Giving another class variablevalues before the variables are declared in Java

I am trying to pass values to variables that probably are not declared yet.
From my main source class I am giving another's class some values, but later on it seems like the values are gone.
Source code:
server.java (main):
public class server {
public static void main(String[] args) {
//Print a simple message to the user to notify there is something going on...
System.out.println("Starting server, please wait...");
//Connecting all class files to the server.
filehandler filehandlerclass = new filehandler();
networking networkingclass = new networking();
//End of class files connecting.
//Preparing the filehandler's file information to open a new filestream.
filehandlerclass.filetohandlename = "server";
filehandlerclass.filetohandleextention = "ini";
filehandlerclass.filetohandlepath = "configs\\";
//Request a new filestream using the filehandler's file variables.
filehandlerclass.openfilestream(filehandlerclass.filestream, filehandlerclass.filetohandle);
//Checks if the filehandler has tried to open a filestream.
if(filehandlerclass.filestreamopen == true) {
//Request a check if the filestream was opened sucessfully.
filehandlerclass.filestreamexists(filehandlerclass.filestream);
}
//If the filehandler has not tried to open a filestream...
else {
System.out.println("Error: The filehandler does not seem to have tried to open a filoestream yet.");
System.out.println("A possibility is that the server could not call the method from the filehandler properly.");
}
//Checks if the boolean "filestreamexists" from the filehandlerclass is true.
if(filehandlerclass.filestreamexists(filehandlerclass.filestream) == true) {
//The filestream seems to exist, let's read the file and extract it's information.
filehandlerclass.readfile(filehandlerclass.filestream);
}
else {
filehandlerclass.openfilestream(filehandlerclass.filestream, filehandlerclass.filetohandle);
}
}
}
filehandler.java:
//Imports the java.io library so the filehandler can read and write to text files.
import java.io.*;
public class filehandler {
//Variables for the filehandler class.
public String filetohandlename;
public String filetohandleextention;
public String filetohandlefullname = filetohandlename + "." + filetohandleextention;
public String filetohandlepath;
public String filetohandle = filetohandlepath + filetohandlefullname;
//Boolean that is true if the filehandler's "openfilestream"-method has tried to open a filestream.
//Is false as long as none filestreams have been touched.
public boolean filestreamopen = false;
//Declares a variable for the filestream to access text files.
public File filestream;
//End of variable list.
//Called to open a filestream so the server can load properties from text files.
public void openfilestream(File filestream, String filetohandle) {
//Tell the user that a filestream is about to be opened.
System.out.println("Opening filestream for \"" + filetohandlefullname + "\"...");
//Open a filestream called "filestream" using the variable "filetohandle"'s value
//as information about wich file to open the filestream for.
filestream = new File(filetohandle);
//Turn the boolean "filestreamopen" to true so next time the server checks it's
//value, it knows if the filehandler has tried to open a filestream.
filestreamopen = true;
}
//Boolean that checks if the filestream exists.
public boolean filestreamexists(File filestream) {
//Tell the user that a check on the filestream is going on.
System.out.println("Checking if filestream for \"" + filetohandlefullname + "\" exists...");
//If the filestream exists...
if(filestream.exists()) {
//Tell the user that the filestream exists.
System.out.println("Filestream for \"" + filetohandlefullname + "\" exists!");
//Make the boolean's value positive.
return true;
}
//If the filestream does not exist...
else {
//Tell the user that the filestream does not exist.
System.out.println("Filestream for \"" + filetohandlefullname + "\" does not exist!");
//Make the boolean's value negative.
return false;
}
}
//Called to read files and collect it's information.
public void readfile(File filestream) {
//Checks if the file that is going to be read is a configuration file.
if(filetohandleextention == "ini") {
//Tell the user that a configuration file is going to be read.
System.out.println("Extracting information from the configuration file \"" + filetohandle + "\".");
}
}
}
networking.java:
public class networking {
}
Problem:
server.java is going to serve commands to the source files and tell them what to do.
The source files are not going to act on their own unless server.java has given them a command.
This way I am planning to be able to write simple function calls in server.java to do greater tasks from the different source files.
server.java seems to pass the variables "filetohandlename", "filetohandleextention" and "filetohandlepath" before the variables are declared and when they get declared, they are declared with "null" as value.
Result:
I get no errors when I compile it.
All I think is happening is a miss match with giving the variables that specifies the file that is going to be read's proper values.
It also throws an exception which I have not been careing to look into for now, either it's because "null.null" does not exist or that I wrote the code wrong.
Final request:
Does anybody know if I can make a method for recieving the variables values
or if there is another more proper way around?
Could I probably make an array of the variables in server.java and collect the values from that array?
Thank you so much for your time.
This:
public String filetohandlename;
public String filetohandleextention;
public String filetohandlefullname = filetohandlename + "." + filetohandleextention;
initialises the first two variables to null, and the third to "null.null". Note that if you change one of the component variables that have made up filetohandlefullname, it won't then change the value of filetohandlefullname. If you want that to happen, then filetohandlefullname should be replaced by a method performing the appending operation.
This:
public void openfilestream(File filestream, String filetohandle)
passes a different variable filetohandle into the method. That variable is distinct from this.filetohandle.
I think there's a numberof issues with this code (above) and I'd do the following.
replace variables instantiated via other variables with methods that perform this dynamically. That way, when you change var1 that you'd expect to change the value of var2, that will happen automatically via a method return. e.g create a private method getFileToHandleFullName() and bin the corresponding variable
scope class members with this
where possible, make those members final so you don't inadvertently change them
This line
public String filetohandlefullname = filetohandlename + "." + filetohandleextention;
is executed when you instantiate the class (ie filehandler filehandlerclass = new filehandler();). At that point in time both variables are unset, thus filetohandlefullname is initialized to null.null
But there's a number of other problems with your code as well, like
//Request a new filestream using the filehandler's file variables.
filehandlerclass.openfilestream(filehandlerclass. filestream,filehandlerclass.filetohandle);
eg you're passing parameters that are fields from the same instance. That's totally useless as the method already has access to those, and it's very confusing.
And maybe slightly controversial:
//Make the boolean's value positive.
return true;
Comments are only useful if they clarify code that without them would be less obvious, and they have to be 100% true and not, as happens so often, what the program wishes the code would do. In this specific case neither of these conditions is fulfilled as the comment doesn't clarify what's going on, and actually the method's return value is set, not some nondescript "boolean's value"

'Un'-externalize strings from Eclipse or Intellij

I have a bunch of strings in a properties file which i want to 'un-externalize', ie inline into my code.
I see that both Eclipse and Intellij have great support to 'externalize' strings from within code, however do any of them support inlining strings from a properties file back into code?
For example if I have code like -
My.java
System.out.println(myResourceBundle.getString("key"));
My.properties
key=a whole bunch of text
I want my java code to be replaced as -
My.java
System.out.println("a whole bunch of text");
I wrote a simple java program that you can use to do this.
Dexternalize.java
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Deexternalize {
public static final Logger logger = Logger.getLogger(Deexternalize.class.toString());
public static void main(String[] args) throws IOException {
if(args.length != 2) {
System.out.println("Deexternalize props_file java_file_to_create");
return;
}
Properties defaultProps = new Properties();
FileInputStream in = new FileInputStream(args[0]);
defaultProps.load(in);
in.close();
File javaFile = new File(args[1]);
List<String> data = process(defaultProps,javaFile);
buildFile(javaFile,data);
}
public static List<String> process(Properties propsFile, File javaFile) {
List<String> data = new ArrayList<String>();
Set<Entry<Object,Object>> setOfProps = propsFile.entrySet();
int indexOf = javaFile.getName().indexOf(".");
String javaClassName = javaFile.getName().substring(0,indexOf);
data.add("public class " + javaClassName + " {\n");
StringBuilder sb = null;
// for some reason it's adding them in reverse order so putting htem on a stack
Stack<String> aStack = new Stack<String>();
for(Entry<Object,Object> anEntry : setOfProps) {
sb = new StringBuilder("\tpublic static final String ");
sb.append(anEntry.getKey().toString());
sb.append(" = \"");
sb.append(anEntry.getValue().toString());
sb.append("\";\n");
aStack.push(sb.toString());
}
while(!aStack.empty()) {
data.add(aStack.pop());
}
if(sb != null) {
data.add("}");
}
return data;
}
public static final void buildFile(File fileToBuild, List<String> lines) {
BufferedWriter theWriter = null;
try {
// Check to make sure if the file exists already.
if(!fileToBuild.exists()) {
fileToBuild.createNewFile();
}
theWriter = new BufferedWriter(new FileWriter(fileToBuild));
// Write the lines to the file.
for(String theLine : lines) {
// DO NOT ADD windows carriage return.
if(theLine.endsWith("\r\n")){
theWriter.write(theLine.substring(0, theLine.length()-2));
theWriter.write("\n");
} else if(theLine.endsWith("\n")) {
// This case is UNIX format already since we checked for
// the carriage return already.
theWriter.write(theLine);
} else {
theWriter.write(theLine);
theWriter.write("\n");
}
}
} catch(IOException ex) {
logger.log(Level.SEVERE, null, ex);
} finally {
try {
theWriter.close();
} catch(IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
}
}
Basically, all you need to do is call this java program with the location of the property file and the name of the java file you want to create that will contain the properties.
For instance this property file:
test.properties
TEST_1=test test test
TEST_2=test 2456
TEST_3=123456
will become:
java_test.java
public class java_test {
public static final String TEST_1 = "test test test";
public static final String TEST_2 = "test 2456";
public static final String TEST_3 = "123456";
}
Hope this is what you need!
EDIT:
I understand what you requested now. You can use my code to do what you want if you sprinkle a bit of regex magic. Lets say you have the java_test file from above. Copy the inlined properties into the file you want to replace the myResourceBundle code with.
For example,
TestFile.java
public class TestFile {
public static final String TEST_1 = "test test test";
public static final String TEST_2 = "test 2456";
public static final String TEST_3 = "123456";
public static void regexTest() {
System.out.println(myResourceBundle.getString("TEST_1"));
System.out.println(myResourceBundle.getString("TEST_1"));
System.out.println(myResourceBundle.getString("TEST_3"));
}
}
Ok, now if you are using eclipse (any modern IDE should be able to do this) go to the Edit Menu -> Find/Replace. In the window, you should see a "Regular Expressions" checkbox, check that. Now input the following text into the Find text area:
myResourceBundle\.getString\(\"(.+)\"\)
And the back reference
\1
into the replace.
Now click "Replace all" and voila! The code should have been inlined to your needs.
Now TestFile.java will become:
TestFile.java
public class TestFile {
public static final String TEST_1 = "test test test";
public static final String TEST_2 = "test 2456";
public static final String TEST_3 = "123456";
public static void regexTest() {
System.out.println(TEST_1);
System.out.println(TEST_1);
System.out.println(TEST_3);
}
}
You may use Eclipse "Externalize Strings" widget. It can also be used for un-externalization. Select required string(s) and press "Internalize" button. If the string was externalized before, it'll be put back and removed from messages.properties file.
May be if you can explain on how you need to do this, then you could get the correct answer.
The Short answer to your question is no, especially in Intellij (I do not know enough about eclipse). Of course the slightly longer but still not very useful answer is to write a plugin. ( That will take a list of property files and read the key and values in a map and then does a regular expression replace of ResourceBundle.getValue("Key") with the value from Map (for the key). I will write this plugin myself, if you can convince me that, there are more people like you, who have this requirement.)
The more elaborate answer is this.
1_ First I will re-factor all the code that performs property file reading to a single class (or module called PropertyFileReader).
2_ I will create a property file reader module, that iterates across all the keys in property file(s) and then stores those information in a map.
4_ I can either create a static map objects with the populated values or create a constant class out of it. Then I will replace the logic in the property file reader module to use a get on the map or static class rather than the property file reading.
5_ Once I am sure that the application performs ok.(By checking if all my Unit Testing passes), then I will remove my property files.
Note: If you are using spring, then there is a easy way to split out all property key-value pairs from a list of property files. Let me know if you use spring.
I would recommend something else: split externalized strings into localizable and non-localizable properties files. It would be probably easier to move some strings to another file than moving it back to source code (which will hurt maintainability by the way).
Of course you can write simple (to some extent) Perl (or whatever) script which will search for calls to resource bundles and introduce constant in this place...
In other words, I haven't heard about de-externalizing mechanism, you need to do it by hand (or write some automated script yourself).
An awesome oneliner from #potong sed 's|^\([^=]*\)=\(.*\)|s#Messages.getString("\1")#"\2"#g|;s/\\/\\\\/g' messages.properties |
sed -i -f - *.java run this inside your src dir, and see the magic.

Categories

Resources