How to construct a file from a relative path in a File - java

I'm parsing a base file at this location:
/Users/haddad/development/fspc/content/2017.dev/src/agency/individual/integration/src/forms/print.xml
And from that file, I parse out:
../../../../include/masking.xml
So that relative path is from the context from the file I parsed it out from (base file)
How can I constuct a file object from that relative path so I can read it's contents?

Since you tagged nio, the Path class makes this easy. You simply call resolveSibling() and normalize().
String main = "/Users/haddad/development/fspc/content/2017.dev/src/agency/individual/integration/src/forms/print.xml";
String ref = "../../../../include/masking.xml";
System.out.println(Paths.get(main));
System.out.println(Paths.get(main).resolveSibling(ref));
System.out.println(Paths.get(main).resolveSibling(ref).normalize());
Or:
System.out.println(Paths.get(main));
System.out.println(Paths.get(main, ref));
System.out.println(Paths.get(main, ref).normalize());
Output
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\print.xml
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\..\..\..\..\include\masking.xml
\Users\haddad\development\fspc\content\2017.dev\src\agency\include\masking.xml
Note: I ran this on a Window machine, so I of course got backslashes
If you prefer the old File object, you use the two-argument constructor, and call getCanonicalFile().
System.out.println(new File(main));
System.out.println(new File(main, ref));
System.out.println(new File(main, ref).getCanonicalFile());
Output
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\print.xml
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\print.xml\..\..\..\..\include\masking.xml
C:\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\include\masking.xml

You could use subpath() to keep the path part that interests you that you can combine with resolve() to append a new path to :
public static void main(String[] args) {
Path printXmlPath = Paths.get("/Users/haddad/development/fspc/content/2017.dev/src/agency/individual/integration/src/forms/print.xml");
Path maskingXmlPath = printXmlPath.subpath(0, printXmlPath.getNameCount() - 5)
.resolve("include/masking.xml");
System.out.println(maskingXmlPath);
}
Users\haddad\development\fspc\content\2017.dev\src\agency\include\masking.xml

Related

Is there a clean and easy way to make file path strings in Java OS agnostic?

I've made a class which takes in any string of one format (eg. UNIX) and coverts into whatever OS the java is running on.
enum OperatingSystem {
WINDOWS,
LINUX;
static OperatingSystem initOS() {
String osName = System.getProperty("os.name");
switch (osName) {
case "Windows 8.1":
return WINDOWS;
case "Linux":
return LINUX;
default:
return LINUX;
}
}
}
public class OSSP {
public static final OperatingSystem USEROS = OperatingSystem.initOS();
// Auxilarry methods to return OSAppropriateString
private static String makeLinuxCompatible(String[] path) {
return String.join("/", path);
}
private static String makeWindowsCompatible(String[] path) {
return String.join("\\", path);
}
public static String getOSSpecificPath(String path) {
String[] splittedPath = {""}, subpath = {""};
String finalPath = "";
if(path.contains("\\")) {
splittedPath = path.split("\\\\",-1);
}
else if (path.contains("/")) {
splittedPath = path.split("/",-1);
}
if (USEROS == OperatingSystem.LINUX) {
finalPath = makeLinuxCompatible(splittedPath);
}
else if (USEROS == OperatingSystem.WINDOWS) {
finalPath = makeWindowsCompatible(splittedPath);
}
return finalPath;
}
}
This is fine if you're working on small code and you'd have to do it often.
But, I have a huge GUI code where I'd have to insert this function wherever there is path specified in the program. Is there a way to make path like strings automatically OS specific?
Otherwise a setting where any OS function which takes a path automatically changes accordingly under the hood.
Use Path with Files.
Path path = Paths.get(".../...");
Path path = Paths.get("...", "...");
// path.resolve, relativize, normalize, getFileSystem
This class is a generalisation of File which is only for pure file system files.
A path might point in a subdirectory of a .zip using a zip file system and so on.
For established File using APIs one can use Path.toFile() and File.toPath().
Paths.get is very versatile, also due to the Posix compatibility of Windows (accepting / besides \). You can get a canonical normalized path anyway.
path.toRealPath()
The old File you can use:
String separator = File.separator;
For a path which can point to different file systems:
String separator = path.getFileSystem().getSeparator();
In general Path is a nifty class storing the name parts, the file system.
It covers many aspects like "..".
The best way to deal with this kind of situation is to not try to detect the OS since that can be rather hit-or-miss. Instead the Java API does provide a way to tell you what character to use as a path separator. Look at this API documentation on File: https://docs.oracle.com/javase/8/docs/api/java/io/File.html and look for the specific static field separator. I would highly suggest you parse the path using the File class then if you need the path as an string simply call toURI().toString() to get it into a format that the OS can recognize.

SAX parser is not working in windows?

i have multiple xml files named media01.xml, media02.xml and so on.
I have written one java code which parses this xml file and fetches its table name and renames xml file. eg: media01--> Records.xml, media02 --> Info.xml and so on.
Part of that code is as follows:
File inputFile = new File(path+File.separator+"media0"+xmlval+".xml");
if(inputFile.exists())
{
try{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
aaaa a= new aaaa();
saxParser.parse(inputFile, a);
String abc = aaaa.nsList();
File dest = new File(path+File.separator+abc+".xml");
inputFile.renameTo(dest);
xmlval++;
}
catch(Exception e)
{
System.err.println(""+e);
}
}
and the function which i am calling is:
class aaaa extends DefaultHandler {
boolean bFirstName = false;
boolean bLastName = false;
boolean loc = false;
String name = null;
static String ans;
#Override
public void startElement(String uri,String localName, String qName, Attributes attributes)
throws SAXException {
if (qName.equalsIgnoreCase("table")) {
name = attributes.getValue("name");
}
if(qName.equalsIgnoreCase("row")){
}
ans=name;
}
public static String nsList(){
return ans;
}
}
i deployed my project on server and when i run the project from ubuntu OS then the xml file names are getting changed but the same when i am running from windows then its not renaming the files. what might be the issue?
Pls help me out. Thanks in advance.
i don't thin it is a Parser problem since there is no problem and SAXParser is used by so many projects that depend on SAX to parse their configuration file such as Spring , jsf i think and so many others so it is unlikely to be a saxproblem so the problem can be i your call to
File dest = new File(path+File.separator+abc+".xml");
inputFile.renameTo(dest);
which is platform dependent instruction you better check if the renaming was done successfully by doing like this
File dest = new File(path+File.separator+abc+".xml");
boolean renameSuccess=inputFile.renameTo(dest);
System.out.println("renaming "+renameSuccess?"succeeded":"failed");
One of the problems I could encounter when deploying an application tested on a system to another system is that path and file names are case sensitive on Unix-like system. It is possible that your file already existed on your target system but with a different case. Anyway, as achabahe mentioned it, you should check your return value when you rename a file.
Another remark, path separators are system dependent but generally Java doesn't make any problem. You can for example use '/' in a Windows path. I just would suggest you to instantiate File objects this way:
File myFile = new File(myPath, myFileName);
This is so easier to read and system-independent.
I also suggest you to trace if you actually open the source file. By the way can't you run it in debug mode?

Java: Passing String variable in new File();

I am developing an desktop application which reads specific XML Elements using XPath and displays them in text fields in a JFrame.
So far, the program ran smoothly until I decided to pass a String variable in the File class.
public void openNewFile(String filePath) {
//file path C:\\Documents and Settings\\tanzim.hasan\\my documents\\xcbl.XML
//is passed as a string from another class.
String aPath = filePath;
//Path is printed on screen before entering the try & catch.
System.out.println("File Path Before Try & Catch: "+filePath);
try {
//The following statement works if the file path is manually written.
// File xmlFile = new File ("C:\\Documents and Settings\\tanzim.hasan\\my documents\\xcbl.XML");
//The following statement prints the actual path
File xmlFile = new File(aPath);
System.out.println("file =" + xmlFile);
//From here the document does not print the expected results.
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
docFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(xmlFile);
doc.getDocumentElement().normalize();
XPath srcPath = XPathFactory.newInstance().newXPath();
XPathShipToAddress shipToPath = new XPathShipToAddress(srcPath, doc);
XPathBuyerPartyAddress buyerPartyPath = new XPathBuyerPartyAddress(srcPath, doc);
} catch (Exception e) {
//
}
}
If I define the xmlFile with a static path (i.e. manually write it) then the program works as expected. However, instead of writing a static path, if I pass the path as a string variable aPath it does not print the expected results.
I have done a bit of googling but failed to find anything concrete.
Just use the builtin object methods:
System.out.println("file = "+xmlFile.toString());
You could also use:
System.out.println("f = " + f.getAbsolutePath());
Also, if you're having issues wit hthe file not existing, check first then proceed:
File file = new File (aPath);
if(file.exists()) {
//Do your work
}
If you are using replaceAll() like this path.replaceAll("\\", "/") to remove the backslashes, it will fail because the replaceAll() method expects a regex as the first parameter and a single backslash (coded as "\\") is an invalid regex. To make it work using replaceAll(), you would need double-escape the backslash (once for the String, again for the regex) like this path.replaceAll("\\\\", "/").
However, you don't need a regex! Instead, use the plain-text based replace() method like this:
path.replace("\\", "/")
Note that the names "replace" and "replaceAll" are misleading: "replace" still replaces all occurrences... the moron that decided on the name "replaceAll" should have chosen "replaceRegex" or something similar
Edit
Try:
path = path.replace("\\\\", "/");
it is too late to answer this but...removing "" from my config file helped
I mean
pathVariable=c:\\some\\path\\here
not this
pathVariable="c:\\some\\path\\here"

Accepting both file paths and URL in Java

Is there a method which would accept the following paths and return appropriate URI:
test.xml // File in CWD
./test.xml // The same
../test.xml // File in parent directory
/etc/test.xml // Absolute path
file:///etc/test.xml // Full URL
http://example.com/test.xml // URL for http
Currently all I can think of is parse as url (URL.create) and if it fails attempt to try to parse it as File/Path.
If you want to use those URIs, for example in File constructor, you need to specify base URI for relative paths. You can do it with URI.resolve.
URI basePath = new URI("file:///base_dir/");
URI uri = basePath.resolve("foo.txt");
System.out.println(new File(uri));
Use URI, not URL.
This may or may not be what you actually want, however, depending on what you need to do with the result.
You can create an URI for each of the resources you pointed as follows:
public class T {
public static void main(final String[] args) throws URISyntaxException {
System.out.println(new URI("test.xml"));
System.out.println(new URI("./test.xml"));
System.out.println(new URI("../test.xml"));
System.out.println(new URI("/etc/test.xml"));
System.out.println(new URI("file:///etc/test.xml"));
System.out.println(new URI("http://example.com/test.xml"));
}
}
Additionally, you can retrieve the URL with the method "toURL()", but this just in case of the URI is absolute.

Generating a canonical path

Does any one know of any Java libraries I could use to generate canonical paths (basically remove back-references).
I need something that will do the following:
Raw Path -> Canonical Path
/../foo/ -> /foo
/foo/ -> /foo
/../../../ -> /
/./foo/./ -> /foo
//foo//bar -> /foo/bar
//foo/../bar -> /bar
etc...
At the moment I lazily rely on using:
new File("/", path).getCanonicalPath();
But this resolves the path against the actual file system, and is synchronised.
java.lang.Thread.State: BLOCKED (on object monitor)
at java.io.ExpiringCache.get(ExpiringCache.java:55)
- waiting to lock <0x93a0d180> (a java.io.ExpiringCache)
at java.io.UnixFileSystem.canonicalize(UnixFileSystem.java:137)
at java.io.File.getCanonicalPath(File.java:559)
The paths that I am canonicalising do not exist on my file system, so just the logic of the method will do me fine, thus not requiring any synchronisation. I'm hoping for a well tested library rather than having to write my own.
I think you can use the URI class to do this; e.g. if the path contains no characters that need escaping in a URI path component, you can do this.
String normalized = new URI(path).normalize().getPath();
If the path contains (or might contain) characters that need escaping, the multi-argument constructors will escape the path argument, and you can provide null for the other arguments.
Notes:
The above normalizes a file path by treating it as a relative URI. If you want to normalize an entire URI ... including the (optional) scheme, authority, and other components, don't call getPath()!
URI normalization does not involve looking at the file system as File canonicalization does. But the flip side is that normalization behaves differently to canonicalization when there are symbolic links in the path.
Using Apache Commons IO (a well-known and well-tested library)
public static String normalize(String filename)
will do exactly what you're looking for.
Example:
String result = FilenameUtils.normalize(myFile.getAbsolutePath());
If you don't need path canonization but only normalization, in Java 7 you can use java.nio.file.Path.normalize method.
According to http://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html:
This method does not access the file system; the path may not locate a file that exists.
If you work with File object you can use something like this:
file.toPath().normalize().toFile()
You could try an algorithm like this:
String collapsePath(String path) {
/* Split into directory parts */
String[] directories = path.split("/");
String[] newDirectories = new String[directories.length];
int i, j = 0;
for (i=0; i<directories.length; i++) {
/* Ignore the previous directory if it is a double dot */
if (directories[i].equals("..") && j > 0)
newDirectories[j--] = "";
/* Completely ignore single dots */
else if (! directories[i].equals("."))
newDirectories[j++] = directories[i];
}
/* Ah, what I would give for String.join() */
String newPath = new String();
for (i=0; i < j; i++)
newPath = newPath + "/" + newDirectories[i];
return newPath;
}
It isn't perfect; it's linear over the number of directories but does make a copy in memory.
Which kind of path is qualified as a Canonical Path is OS dependent.
That's why Java need to check it on the filesystem.
So there's no simple logic to test the path without knowing the OS.
So, while normalizing can do the trick, here is a procedure that exposes a little more of the Java API than would simply calling Paths.normalize()
Say I want to find a file that is not in my current directory on the file system.
My working code file is
myproject/src/JavaCode.java
Located in myproject/src/. My file is in
../../data/myfile.txt
I'm testing my program running my code from JavaCode.java
public static void main(String[] args) {
findFile("../../data","myfile.txt");
System.out.println("Found it.");
}
public static File findFile(String inputPath, String inputFile) {
File dataDir = new File("").getAbsoluteFile(); // points dataDir to working directory
String delimiters = "" + '\\' + '/'; // dealing with different system separators
StringTokenizer st = new StringTokenizer(inputPath, delimiters);
while(st.hasMoreTokens()) {
String s = st.nextToken();
if(s.trim().isEmpty() || s.equals("."))
continue;
else if(s.equals(".."))
dataDir = dataDir.getParentFile();
else {
dataDir = new File(dataDir, s);
if(!dataDir.exists())
throw new RuntimeException("Data folder does not exist.");
}
}
return new File(dataDir, inputFile);
}
Having placed a file at the specified location, this should print "Found it."
I'm assuming you have strings and you want strings, and you have Java 7 available now, and your default file system uses '/' as a path separator, so try:
String output = FileSystems.getDefault().getPath(input).normalize().toString();
You can try this out with:
/**
* Input Output
* /../foo/ -> /foo
* /foo/ -> /foo
* /../../../ -> /
* /./foo/./ -> /foo
* //foo//bar -> /foo/bar
* //foo/../bar -> /bar
*/
#Test
public void testNormalizedPath() throws URISyntaxException, IOException {
String[] in = new String[]{"/../foo/", "/foo/", "/../../../", "/./foo/./",
"//foo/bar", "//foo/../bar", "/", "/foo"};
String[] ex = new String[]{"/foo", "/foo", "/", "/foo", "/foo/bar", "/bar", "/", "/foo"};
FileSystem fs = FileSystems.getDefault();
for (int i = 0; i < in.length; i++) {
assertEquals(ex[i], fs.getPath(in[i]).normalize().toString());
}
}

Categories