Changing working directory in Scala [duplicate] - scala

How can I change the current working directory from within a Java program? Everything I've been able to find about the issue claims that you simply can't do it, but I can't believe that that's really the case.
I have a piece of code that opens a file using a hard-coded relative file path from the directory it's normally started in, and I just want to be able to use that code from within a different Java program without having to start it from within a particular directory. It seems like you should just be able to call System.setProperty( "user.dir", "/path/to/dir" ), but as far as I can figure out, calling that line just silently fails and does nothing.
I would understand if Java didn't allow you to do this, if it weren't for the fact that it allows you to get the current working directory, and even allows you to open files using relative file paths....

There is no reliable way to do this in pure Java. Setting the user.dir property via System.setProperty() or java -Duser.dir=... does seem to affect subsequent creations of Files, but not e.g. FileOutputStreams.
The File(String parent, String child) constructor can help if you build up your directory path separately from your file path, allowing easier swapping.
An alternative is to set up a script to run Java from a different directory, or use JNI native code as suggested below.
The relevant OpenJDK bug was closed in 2008 as "will not fix".

If you run your legacy program with ProcessBuilder, you will be able to specify its working directory.

There is a way to do this using the system property "user.dir". The key part to understand is that getAbsoluteFile() must be called (as shown below) or else relative paths will be resolved against the default "user.dir" value.
import java.io.*;
public class FileUtils
{
public static boolean setCurrentDirectory(String directory_name)
{
boolean result = false; // Boolean indicating whether directory was set
File directory; // Desired current working directory
directory = new File(directory_name).getAbsoluteFile();
if (directory.exists() || directory.mkdirs())
{
result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null);
}
return result;
}
public static PrintWriter openOutputFile(String file_name)
{
PrintWriter output = null; // File to open for writing
try
{
output = new PrintWriter(new File(file_name).getAbsoluteFile());
}
catch (Exception exception) {}
return output;
}
public static void main(String[] args) throws Exception
{
FileUtils.openOutputFile("DefaultDirectoryFile.txt");
FileUtils.setCurrentDirectory("NewCurrentDirectory");
FileUtils.openOutputFile("CurrentDirectoryFile.txt");
}
}

It is possible to change the PWD, using JNA/JNI to make calls to libc. The JRuby guys have a handy java library for making POSIX calls called jnr-posix. Here's the maven info

As mentioned you can't change the CWD of the JVM but if you were to launch another process using Runtime.exec() you can use the overloaded method that lets you specify the working directory. This is not really for running your Java program in another directory but for many cases when one needs to launch another program like a Perl script for example, you can specify the working directory of that script while leaving the working dir of the JVM unchanged.
See Runtime.exec javadocs
Specifically,
public Process exec(String[] cmdarray,String[] envp, File dir) throws IOException
where dir is the working directory to run the subprocess in

If I understand correctly, a Java program starts with a copy of the current environment variables. Any changes via System.setProperty(String, String) are modifying the copy, not the original environment variables. Not that this provides a thorough reason as to why Sun chose this behavior, but perhaps it sheds a little light...

The working directory is a operating system feature (set when the process starts).
Why don't you just pass your own System property (-Dsomeprop=/my/path) and use that in your code as the parent of your File:
File f = new File ( System.getProperty("someprop"), myFilename)

The smarter/easier thing to do here is to just change your code so that instead of opening the file assuming that it exists in the current working directory (I assume you are doing something like new File("blah.txt"), just build the path to the file yourself.
Let the user pass in the base directory, read it from a config file, fall back to user.dir if the other properties can't be found, etc. But it's a whole lot easier to improve the logic in your program than it is to change how environment variables work.

I have tried to invoke
String oldDir = System.setProperty("user.dir", currdir.getAbsolutePath());
It seems to work. But
File myFile = new File("localpath.ext");
InputStream openit = new FileInputStream(myFile);
throws a FileNotFoundException though
myFile.getAbsolutePath()
shows the correct path.
I have read this. I think the problem is:
Java knows the current directory with the new setting.
But the file handling is done by the operation system. It does not know the new set current directory, unfortunately.
The solution may be:
File myFile = new File(System.getPropety("user.dir"), "localpath.ext");
It creates a file Object as absolute one with the current directory which is known by the JVM. But that code should be existing in a used class, it needs changing of reused codes.
~~~~JcHartmut

You can use
new File("relative/path").getAbsoluteFile()
after
System.setProperty("user.dir", "/some/directory")
System.setProperty("user.dir", "C:/OtherProject");
File file = new File("data/data.csv").getAbsoluteFile();
System.out.println(file.getPath());
Will print
C:\OtherProject\data\data.csv

You can change the process's actual working directory using JNI or JNA.
With JNI, you can use native functions to set the directory. The POSIX method is chdir(). On Windows, you can use SetCurrentDirectory().
With JNA, you can wrap the native functions in Java binders.
For Windows:
private static interface MyKernel32 extends Library {
public MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class);
/** BOOL SetCurrentDirectory( LPCTSTR lpPathName ); */
int SetCurrentDirectoryW(char[] pathName);
}
For POSIX systems:
private interface MyCLibrary extends Library {
MyCLibrary INSTANCE = (MyCLibrary) Native.loadLibrary("c", MyCLibrary.class);
/** int chdir(const char *path); */
int chdir( String path );
}

The other possible answer to this question may depend on the reason you are opening the file. Is this a property file or a file that has some configuration related to your application?
If this is the case you may consider trying to load the file through the classpath loader, this way you can load any file Java has access to.

If you run your commands in a shell you can write something like "java -cp" and add any directories you want separated by ":" if java doesnt find something in one directory it will go try and find them in the other directories, that is what I do.

Use FileSystemView
private FileSystemView fileSystemView;
fileSystemView = FileSystemView.getFileSystemView();
currentDirectory = new File(".");
//listing currentDirectory
File[] filesAndDirs = fileSystemView.getFiles(currentDirectory, false);
fileList = new ArrayList<File>();
dirList = new ArrayList<File>();
for (File file : filesAndDirs) {
if (file.isDirectory())
dirList.add(file);
else
fileList.add(file);
}
Collections.sort(dirList);
if (!fileSystemView.isFileSystemRoot(currentDirectory))
dirList.add(0, new File(".."));
Collections.sort(fileList);
//change
currentDirectory = fileSystemView.getParentDirectory(currentDirectory);

Related

The type or namespace name 'Forms' does not exist in the namespace 'System.Windows' FIX

Can someone please help me with this, am trying to use OpenFileDialog class from System.Windows.Forms to open a file dialog and read the selected file. Then, this error showed up. I've referenced it but still the same, below is the code.
`using UnityEngine
using UnityEngine.UI
using System.Windows.Forms;
public class OpenFileButtonScript : MonoBehaviour
{
public TextFieldScript textFieldScript;
public void OpenFile()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
openFileDialog.FilterIndex = 1;
openFileDialog.Multiselect = false;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string filePath = openFileDialog.FileName;
string text = System.IO.File.ReadAllText(filePath);
textFieldScript.inputField.text = text;
}
}
}`
It may look like you have access to all of the native Window system libraries, but it just looks like it. In actuality, a lot of the time you're simply given stubs, or shims, that look like the full Window libraries, because there's a certain element that Unity wants to use from those namespaces. If you think about it, the code you present above, what do you think it should do on Android or Nintendo devices? The simple answer is, it simply won't work.
Generally in cases like this, you have to gain access to the native operating system, and perform those calls directly. For example, there is a file browser asset on the Asset Store, that does this for you. It's not free, because the process isn't trivial.
Depending on how much effort you want to put in, you CAN read files from the local file stores (to varying degrees based on platform). It's possible to read the list of files in a location, and use either uGUI or UIToolkit to create your own File Open Dialogue box. Again, this isn't a trivial task either. So you have to be sure that you'd want to go down that path.

How to exclude UnityEditor reference from asmdef?

How to exclude UnityEditor reference from asmdef?
Why I need it:
I have an asmdef file. For example, it is MyAssembly/MyAssembly.asmdef. The MyAssembly contains a lot of features and each feature staff is placed in its own folder. And some of these features has a code that is needed only in editor, and it refers to UnityEditor namespace. Such editor code is placed into an Editor folder.
But as you know, Editor folder name means nothing in terms of asmdef usage. So I add AssemblyDefenitionReference in each folder and refer it to the MyAssemblyEditor.asmdef assembly definition. So the paths looks like this:
MyAssembly/MyAssembly.asmdef
MyAssembly/Editor/MyAssemblyEditor.asmdef - this folder contains no code. It's needed just to place asmdef, because it's not allowed to place two asmdefs in a single folder.
MyAssembly/SomeFeature/Editor/*feature editor staff*
MyAssembly/SomeFeature/Editor/Editor.asmref - refers to MyAssemblyEditor.asmdef
MyAssembly/SomeFeature/*feature staff*
All this works good. But the problem is that, when some developer adds a new feature, he can forget to add a reference to the MyAssemblyEditor.asmdef in the editor folder. And there are no any errors will be shown in this case. This mistake will be revealed only when the build will be cooked. But I'd like that using of UnityEditor in MyAssembly will be instantly marked as an error.
Feel free to suggest other solution for this problem.
This thread got me thinking I can use CsprojPostprocessor to remove all references to UnityEditor from my csproj file. I wrote such class:
using System.Text.RegularExpressions;
using UnityEditor;
// ReSharper disable once CheckNamespace
public class CsprojPostprocessor : AssetPostprocessor
{
public static string OnGeneratedCSProject(string path, string content)
{
if (!path.EndsWith("Editor.csproj") && !path.EndsWith("Tests.csproj"))
{
var newContent =
Regex.Replace(content, "<Reference Include=\"UnityEditor(.|\n)*?</Reference>", "");
return newContent;
}
return content;
}
}
It also can be done with an xml parser or something.
The only thing, that confuse me is that this mechanism is badly documented and doesn't look like something simple users should use. So I use it at my own risk, but looks like there is no guarantee it will be strongly supported in future.

Debugging Groovy scripts running in a ScriptEngine from Eclipse

I have a Groovy script which is run like this:
File scriptFile = ...;
ScriptEngine engine = ...;
String script = FileUtils.readFileToString(scriptFile);
Object evalResult = engine.eval(script, bindings);
Unsurprisingly, breakpoint set in the script file doesn't trigger. What can I change to make it work? The script needs to be run in the context of the larger program (no separate launch configuration), and through a ScriptEngine, and the file is only known at runtime.
I'm using this hack: I've defined a Java class Debugger which looks like this:
public class Debugger {
private static final Logger log = LoggerFactory.getLogger( Debugger.class );
/**
* Invoke this method anywhere to end up in the Java debugger.
*
* <p>Sometimes, you have code that might eventually be executed or you have a place
* where you want to stop but no code line.
*
* <p>In these case, use this method.
*
* <p>Don't forget to set a breakpoint, first :-)
* */
public static void stopInDebugger() {
log.error( "Please set a breakpoint in Debugger.stopInDebugger() and try again whatever you just did!" );
}
}
I have a breakpoint in the log.error line in Ecipse.
Now, I can put this line into the script where I want a breakpoint:
Debugger.stopInDebugger();
Granted, it doesn't allow me to easily step through the script but it's better than nothing.
Is your script file in a source folder on the classpath (sounds like it's not)? If not, make it so. You can also change your preferences to ensure that the script file is never compiled by the compiler (and optionally not even copied to the output folder). Go to Preferences -> Groovy -> Compiler and look at script folders to make this happen.

Eclipse OSGI config: relative paths and/or #config.dir-like substitutions?

In my RCP app, I would like to point a property (osgi.java.profile) to a file, and would prefer using paths relative to my installation and config dir.
Is there a definitive spec on what kind of variables are supported in config.ini?
#config.dir seems to be supported, there are references in the builtin, and it's always mentioned as typical example (e.g this SO answer )
However, looking at docs like Eclipse help/Runtime Options, it mentions a few "symbolic locations" like #user.home; however that seems fairly limited and doesn't include #config.dir.
Have even dug into org.eclipse.osgi sources as well, and found no references to this (I did find LocationManager and its hard coded variable substitutions for #user.dir & co).
Can I refer to arbitrary system properties there in some way?
Is this #config.dir a special case, only handled by P2? UPDATE: this seems to be the case.. looking at Eclipse SDK, About .. Configuration dialog shows #config.dir unresolved, probably taken literally by the Equinox..
Thanks for any hints.
I'm late to the party, but hopefully this will help others in the future.
Starting with Eclipse 3.8/4.2 (June 2012), you can substitute Java properties and environment variables into your config.ini file (Eclipse Bug 241192). The Equinox launcher does not support substitution in the eclipse.ini launcher file. The syntax uses dollar signs ($VARIABLE$) to indicate variable substitution:
osgi.configuration.area=$APPDATA$/MyCompany/MyProgram/configuration
osgi.user.area=$APPDATA$/MyCompany/MyProgram/user
osgi.instance.area=$APPDATA$/MyCompany/MyProgram/instance
I imagine you could use something like this for your purposes:
osgi.java.profile=$osgi.install.area$/path/to/profile.txt
You can use a platform URL (Platform URI scheme) to achieve this, i.e.
osgi.java.profile = platform:/config/java_profile.txt
in config.ini, would point to the file java_profile.txt in the current configuration directory.
You might also use existing system properties in config.ini:
osgi.java.profile = ${osgi.configuration.area}/java_profile.txt
From org.eclipse.core.runtime.adaptor.LocationManager, here are the special tokens:
// Data mode constants for user, configuration and data locations.
private static final String NONE = "#none"; //$NON-NLS-1$
private static final String NO_DEFAULT = "#noDefault"; //$NON-NLS-1$
private static final String USER_HOME = "#user.home"; //$NON-NLS-1$
private static final String USER_DIR = "#user.dir"; //$NON-NLS-1$
Why not use two system property variables?
One is named -Dmy.relativepath=filename, which is processed by your code of relative path of eclipse installation folder(workspace or anywhere), another is called -Dmy.path=absolutepath.
The system property is passed to the jvm, you need some tricky(translate the variable in runtime) in the native launcher(like eclipse.exe) if you wants to use variable in its value.
Look how osgi.java.profile is resolved in org.eclipse.osgi.framework.internal.core.Framework:
// check for the java profile property for a url
String propJavaProfile = FrameworkProperties.getProperty(Constants.OSGI_JAVA_PROFILE);
if (propJavaProfile != null)
try {
// we assume a URL
url = new URL(propJavaProfile);
} catch (MalformedURLException e1) {
// try using a relative path in the system bundle
url = findInSystemBundle(propJavaProfile);
}
That means osgi.java.profile must point either to a fully qualified URL, or to a relative path in system bundle (org.eclipse.osgi). This makes impossible usage of installation directory relative path without patching Eclipse.

How can I Diff a Svn Repository using SharpSvn

My question is quite simple and with the SharpSvn Api, it should be easy as well. Here what I did:
path = "c:\project";
using (SvnLookClient client = new SvnLookClient())
{
SvnLookOrigin o = new SvnLookOrigin(path);
Collection<SvnChangedEventArgs> changeList;
client.GetChanged(o, out changeList); // <-- Exception
}
and when I call the GetChanged, I get an exception:
Can't open file 'c:\project\format': The system cannot find the file specified.
So, Maybe there is something I'm missing? Or maybe it's not the right way to do find out the list of files and folders that were modified in the local repository?
Thanks in advance.
The SvnLookClient class in SharpSvn is the equivalent to the 'svnlook' console application. It is a low level tool that enables repository hooks to look into specific transactions of a repository using direct file access.
You probably want to use the SvnClient class to look at a WorkingCopy and most likely its Status() or in some cases simpler GetStatus() function to see what changed.
The path that the SvnLookOrigin constructor wants is actually:
path = "c:\project\.svn\";
That is, it wants that special ".svn" directory not just the root of where the source is checked out to.
Although you probably do want to listen to Bert and do something like:
path = "c:\project";
using (SvnLookClient client = new SvnLookClient())
{
SvnLookOrigin o = new SvnLookOrigin(path);
Collection<SvnChangedEventArgs> changeList;
client.GetStatus(o, out changeList); // Should now return the differences between this working copy and the remote status.
}