Why does Path.relativize behave differently on Java 8 and Java 11? - java

Why does the method relativize behave differently on java-8 and java-11?
Path path1 = Paths.get("/a/./b/../image.png");
Path path2 = Paths.get("/a/file.txt");
Path path = path1.relativize(path2);
System.out.println(path);
java-8 (1.8.0_66 to be exact) prints ../../../../file.txt. JavaDoc.
java-11 (11.0.4 to be exact) prints ../file.txt. JavaDoc.
The JavaDoc description of both versions is equal. I feel the java-11 way looks like a correct behavior to me:
path1: /a/./b/../image.png normalizes to /a/b/../image.png which normalizes to /a/image.png
path2: /a/file.txt
the way to navigate from /a/image.png and /a/file.txt is ../file.txt
Questions
How is the java-8 way supposed to be calculated? Doesn't it normalize the path? I don't understand how to get the result from head.
Why is there a difference between these two versions that is not documented at all?

Maybe this bug will answer your question:
https://bugs.openjdk.java.net/browse/JDK-8066943
This affected relativizing paths containing . or .. and was fixed for Java 9.
So that's why you see a difference between 8 and 11.

Windows based source-code answer here.
From the observation of the source codes (let's take a look at sun.nio.fs.WindowsPath, one of the implementations of Path) in java-11 is has additional code including normalization compared to java-8.
java-8 sun.nio.fs.WindowsPath source code at GitHub
java-11 sun.nio.fs.WindowsPath source code at GitHub
The key line of the latter implementation starts at the line 411, so basically, the latter implementation normalizes the paths before taking into calculation of the relative path:
WindowsPath base = this;
if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) {
base = base.normalize();
child = child.normalize();
}
Digging further, the implementation changes between jdk8-b120 (source) and jdk-9+95 (source). Since the modular system was introduced, both the classes implementation and location differ:
Java 8 and below: /jdk/src/windows/classes/sun/nio/fs/WindowsPath.java
Java 9 and above: /jdk/src/java.base/windows/classes/sun/nio/fs/WindowsPath.java
How is the java-8 way supposed to be calculated? Doesn't it normalize the path? I don't understand how to get the result from head.
The most straightforward way to go is to normalize both paths first before relativizing them. But I have no idea whether it completely covers all the java.nio.file.Path implementations and is safe to do so.
Path path = path1.normalize().relativize(path2.normalize());

Related

Java location path regex split

I've "inherited" existing Java Selenium & Cucumber framework which was written mostly for OS usage. I'm using Windows and I'm trying to fix & run it on Windows.
My first problem is specifing corrent file path, this is how it was written for OS:
private String getProjectName(Scenario scenario) {
return Arrays.asList(scenario.getUri().getPath().replace(System.getProperty("user.dir"), "").split("/")).get(5);
}
Error which I'm receiving is:
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 1
As for Windows we're using backlashes I've tried switching "/" into "" but as error appears (+ after my investigations) I've tried with "\\\\" but actually error remains the same as above.
I'm aware that providing only portion of my code and it may be hard but for the first glance can you tell me:
If that method may work on Windows or this should be completely refactored?
Is System.getProperty("user.dir") correct solution?
How to correctly pass backslashes?
Why they're taking .get(5)?
I can guess:
This method is taking the project name that is likely the name of a certain folder in the folder structure where scenario file located.
This is why they took 5th element. Because on the 5th level there was the folder which represented the project.
The used approach look very arguable. At least because there are some redundent steps like converting to list.
Now. How would you go:
The proper way is to use java.nio.file.Path (starts from Java 7) that takes care of differnt OS-specific things.
So your code might look like:
private String getProjectName(Scenario scenario) {
return Path.of(scenario.getUri()).getName(5)
}
P.S. - of course you have to change 5 to catch a proper position of the required folder in your structure.

Work around for adding CNH as a valid currency in java.util.currency [duplicate]

For example, the Chinese currency has the ISO 4217 code CNY. Since free global trading in that currency is restricted though, there's a second 'offshore' currency equivalent, called CNH. Wikipedia has a bit of summary of this all.
In Java 7, there's a method for updating the set of three letter ISO 4217 codes that the JVM ships with. However, it can't be used to add a separate currency code to an existing country code: it would replace CNY with CNH, which is no good for my purposes.
How do I add CNH (which is not in the ISO 4217 list) to the set of available currencies in Java 7, without overwriting CNY?
Put another way, how can I get multiple currency codes for a single country?
Note that this question: How do I add the new currency code to Java? was asked and answered for Java 6. But the strategy of replacing java.util.CurrencyData doesn't work because that file no longer exists.
The key here is in a change that's part of Java 7 to allow updating of the list of currencies without rebuilding rt.jar by replacing a file called currency.data. Using this approach, rather than the currency.properties override approach, allows you to add new Currency codes without affecting other ones from the same country.
What's left unsaid there is how to go about actually building a new currency.data.
This file is generated from a file called CurrencyData.properties, which can be found in the OpenJDK source code in java/util.
What I did was copy the CurrencyData.properties found in the OpenJDK source (openjdk\jdk\src\share\classes\java\util), and changed the line:
BZD084-CAD124-CDF976-CHF756-CLF990-CLP152-CNY156-COP170-CRC188-CSD891-CUP192-\
to
BZD084-CAD124-CDF976-CHF756-CLF990-CLP152-CNH156-CNY156-COP170-CRC188-CSD891-CUP192-\
Then I grabbed the GenerateCurrencyData.java file in the source distribution at openjdk\jdk\make\tools\src\build\tools\generatecurrencydata. This utility takes input from System.In in the same format as CurrencyData.properties, and turns it in to a currency.data file. I made a slight change so that it used a FileInputStream instead of System.In:
currencyData.load(System.in);
to
currencyData.load(new FileInputStream(fileName));
Run that on your edited CurrencyData.properties file and, after putting the original .data file somewhere safe, place the resulting currency.data file in to your JRE\lib directory, and you can now run code that uses Currency.getInstance("CNH").
To the #sharakan answer:
You may also need to add newly changed currency, in my case BYN
BY=BYR;2016-07-01-00-00-00;BYN
in format:
OLD_CUR;DATE_OF_CHANGE;NEW_CUR
Hope it will help someone.

Whirlpool hash in java and in python give different results

I have two projects. panager and panager-android. I use the whirlpool hash algorithm and with the same data panager gives different results than panager-android.
panager is written in python and panager-android (guess) in java.
I'm ultra-new in java so take it easy :P
In python I use a module that I found on the net (whirlpool.py) and in java I use the jacksum library.
There are different versions of the Whirlpool spec which generate different output for the same input. It looks like whirlpool.py might be implementing the original Whirlpool (referred to as "Whirlpool-0"), whereas in panager-android you use Whirlpool-2:
AbstractChecksum encode = JacksumAPI.getChecksumInstance("whirlpool2");
Try changing that to "whirlpool0" and see if it matches your Python implementation now. Failing that, try "whirlpool1".
Wikipedia has known Whirlpool hashes from each version for a given test input which you may use to identify the version of a questioned Whirlpool implementation, or find out if it's just entirely wrong and broken.

How do I add a new Currency to java.util.Currency for an existing country code in Java 7?

For example, the Chinese currency has the ISO 4217 code CNY. Since free global trading in that currency is restricted though, there's a second 'offshore' currency equivalent, called CNH. Wikipedia has a bit of summary of this all.
In Java 7, there's a method for updating the set of three letter ISO 4217 codes that the JVM ships with. However, it can't be used to add a separate currency code to an existing country code: it would replace CNY with CNH, which is no good for my purposes.
How do I add CNH (which is not in the ISO 4217 list) to the set of available currencies in Java 7, without overwriting CNY?
Put another way, how can I get multiple currency codes for a single country?
Note that this question: How do I add the new currency code to Java? was asked and answered for Java 6. But the strategy of replacing java.util.CurrencyData doesn't work because that file no longer exists.
The key here is in a change that's part of Java 7 to allow updating of the list of currencies without rebuilding rt.jar by replacing a file called currency.data. Using this approach, rather than the currency.properties override approach, allows you to add new Currency codes without affecting other ones from the same country.
What's left unsaid there is how to go about actually building a new currency.data.
This file is generated from a file called CurrencyData.properties, which can be found in the OpenJDK source code in java/util.
What I did was copy the CurrencyData.properties found in the OpenJDK source (openjdk\jdk\src\share\classes\java\util), and changed the line:
BZD084-CAD124-CDF976-CHF756-CLF990-CLP152-CNY156-COP170-CRC188-CSD891-CUP192-\
to
BZD084-CAD124-CDF976-CHF756-CLF990-CLP152-CNH156-CNY156-COP170-CRC188-CSD891-CUP192-\
Then I grabbed the GenerateCurrencyData.java file in the source distribution at openjdk\jdk\make\tools\src\build\tools\generatecurrencydata. This utility takes input from System.In in the same format as CurrencyData.properties, and turns it in to a currency.data file. I made a slight change so that it used a FileInputStream instead of System.In:
currencyData.load(System.in);
to
currencyData.load(new FileInputStream(fileName));
Run that on your edited CurrencyData.properties file and, after putting the original .data file somewhere safe, place the resulting currency.data file in to your JRE\lib directory, and you can now run code that uses Currency.getInstance("CNH").
To the #sharakan answer:
You may also need to add newly changed currency, in my case BYN
BY=BYR;2016-07-01-00-00-00;BYN
in format:
OLD_CUR;DATE_OF_CHANGE;NEW_CUR
Hope it will help someone.

Is there a Java equivalent of Python's printf hash replacement?

Specifically I am converting a python script into a java helper method. Here is a snippet (slightly modified for simplicity).
# hash of values
vals = {}
vals['a'] = 'a'
vals['b'] = 'b'
vals['1'] = 1
output = sys.stdout
file = open(filename).read()
print >>output, file % vals,
So in the file there are %(a), %(b), %(1) etc that I want substituted with the hash keys. I perused the API but couldn't find anything. Did I miss it or does something like this not exist in the Java API?
You can't do this directly without some additional templating library. I recommend StringTemplate. Very lightweight, easy to use, and very optimized and robust.
I doubt you'll find a pure Java solution that'll do exactly what you want out of the box.
With this in mind, the best answer depends on the complexity and variety of Python formatting strings that appear in your file:
If they're simple and not varied, the easiest way might be to code something up yourself.
If the opposite is true, one way to get the result you want with little work is by embedding Jython into your Java program. This will enable you to use Python's string formatting operator (%) directly. What's more, you'll be able to give it a Java Map as if it were a Python dictionary (vals in your code).

Categories

Resources