my structure is as follows
MyRootFolder
└──subfolder1
└──subfolder2
.
.
.
└──subfolder n
I have a requirement where-in I need to check if a sub-folder exists within a root folder and if not create it. I can't find a direct API to check for the sub-folder existence. Instead, I see an API like
folder.get_SubFolders();
which would give me a list of all the sub-folders and then iterate to check if sub-folder exists or not. The problem here is that I might end up having to iterate many folders which I don't want to do. Is there a different way to achieve this? I'm using Filenet 5.2.1
ok, this is the closest option that I could find.
Search in FileNet for the subfolder using below query.
SELECT FolderName FROM Folder WHERE FolderName='subfolder1' and Parent=OBJECT({parent-folder-guid})
If the above search returns a result, the subfolder is present if not create one.
Another approach could be - you try to create a folder you want, if already exists dont create it else create it. To achieve it you dont have to iterate through the existing folders. I had the same requirement for one of my project where I had to move the content from shared drive to FileNet P8 and assigning properties to them while maintaining the same folder paths. Following snippet might help someone:
public static Folder createFolderStructure() {
Connection conn = Factory.Connection.getConnection(ConfigInfo.CE_URI);
Domain dom = Factory.Domain.getInstance(conn, null);
ObjectStore os = Factory.ObjectStore.getInstance(dom,
ConfigInfo.OBJECT_STORE_NAME);
Folder folder = null;
// pass your desired folder string here, excluding the starting root "/"
String folderpath = "APITest/some/folder/2014/12/2";
System.out.println("\nGiven input folder path is :: " + folderpath + "\n");
String[] myarray = folderpath.split("/");
for (int x = 0; x < myarray.length; x++) {
try {
if (x == 0) {
folder = Factory.Folder.createInstance(os, null);
Folder rootFolder = Factory.Folder.getInstance(os, null, "/");
folder.set_Parent(rootFolder);
folder.set_FolderName(myarray[x]);
System.out.println("Creating main (first) folder.. \t" + myarray[x]);
folder.save(RefreshMode.NO_REFRESH);
} else {
String currentfolder = myarray[x];
String parentfolder = "";
for (int i = 0; i < x; i++) {
folder = Factory.Folder.createInstance(os, null);
parentfolder = parentfolder + "/" + myarray[i];
Folder nxtrootFolder = Factory.Folder.getInstance(os, null, parentfolder);
folder.set_Parent(nxtrootFolder);
folder.set_FolderName(currentfolder);
}
System.out
.println("Trying to create " + currentfolder + " in " + parentfolder);
folder.save(RefreshMode.NO_REFRESH);
}
} catch (EngineRuntimeException ere) {
ExceptionCode code = ere.getExceptionCode();
if (code != ExceptionCode.E_NOT_UNIQUE) {
throw ere;
}
System.out.println("Above folder already exists...skipping...");
}
}
return folder;
}
This is copied from my own blog.
Related
This is a problem I have encountered in a tech interview. You have 500,000 files in a directory, which is configured so that they are always in alphabetical order. They have names as such:
Afile
Bfile
File00000001
File00000002
...
You want to rename all the files while preserving their order as such:
File00000001
File00000002
File00000003
...
You can probably see the obvious issue here. If you rename Afile into File00000001, it will collide with the existing file with the same name and also the order will be altered, which is not what we want.
The question here is, how can you devise an algorithm with the most optimal run-time to do the renaming task efficiently?
You cannot go through the files in ascending order and also not in decending order, both could lead to a conflict. Also renaming the files to something else first could potentially lead to a conflict. The goal seems to be to rename each file only once, so you can do something as follows:
private static File dir;
public static void renameFiles(String path) {
dir = new File(path);
File[] files = dir.listFiles();
Map<String, String> map = new HashMap<>();
int number = 1;
for (int i = 0; i < files.length; i++)
if (files[i].isFile())
map.put(files[i].getName(), "File" + pad(number++));
// so we created a map with original file names and the name it should get
for (int i = 0; i < files.length; i++)
if (!files[i].getName().equals(map.get(files[i].getName())) // not same name
renameFile(files[i].getName(), map);
}
private static void renameFile(String file, Map<String, String> map) {
String newName = map.get(file);
if (newName != null) {
if (map.containsKey(newName))
renameFile(newName, map)
File f = new File(dir, file);
f.renameTo(new File(dir, newName));
map.remove(file);
}
}
Time complexity O(n). We recursively go ahead until we don't have a renaming conflict any more and then start renaming from the tail. There won't be a conflict because it is possible that File004 becomes File007 or that File007 becomes File004 but not both, so no circular renaming. If there are too many files then recursion depth might not be sufficient and we have to implement it with a stack, but it is the same principle.
private static void renameFile(String file, Map<String, String> map) {
String newName = map.get(file);
if (newName != null) {
Stack<String> stack = new Stack<>();
do {
stack.push(file);
file = newName;
newName = map.get(file);
} while (newName != null);
while (!stack.empty()) {
file = stack.pop();
File f = new File(dir, file);
f.renameTo(new File(dir, map.get(file)));
map.remove(file);
}
}
}
This will work on Linux, but for Windows you could still have problems, because the file names are not case sensitive. You could store all the keys in the map as lower case and always call toLowerCase() when accessing the map.
for i in {100..1..-1} ; do o=$(printf "File%04d" $i); n=$(printf "File%04d" $((i + 2))); echo mv $o $n; done;
or better readable:
for i in {100..1..-1}
do
o=$(printf "File%04d" $i)
n=$(printf "File%04d" $((i + 2)))
echo mv $o $n
done
FileA and FileB can be renamed by hand.
You have to adapt the size, but for testing, a human number of files seemed more appropriate to me.
Ah, yes, that's bash-syntax; important to notice. And it doesn't mv files yet, only echos the mv-command.
Don't try to run it in parallel. :)
But you could as well move them in opposite, normal order to a new dir, and then move them all back into the old dir, to prevent overriding. This would allow to perform it in parallel.
The for-statement is equivalent to what is elsewhere known as
for (i = 100; i >=1; --i)
and
printf "File%04d" $i
prints a 4 digit i with leading zeros.
EDIT - Revised the title of my post to make it more relevant to the problem.
I have folders that may or may not contain subfolders that start with "REV". If there are subfolders that start with "REV" they are followed by an integer value padded with leading zeros. (ie: REV010 or REV003).
My goal here is to:
find the test folder (C:\temp\TEST\REV003)
Read the string name of the folder and parse its integer value
Add the integer to a list of integers. Find the max integer value
Increment the max integer value
Create a new string folder name starting with "REV" and padded with new int value
When I debug the code (below), it cannot seem to find the REV003 folder (The folder definitely exists in the path).
Is something wrong with my LINQ statement in finding the folder?
Also, if there is an easier procedure to achieve the same thing - I'm definitely open for it! Thanks!
int nextRev = 0;
List<int> listOfRevs = new List<int>();
IEnumerable<string> revFolders = Directory.GetDirectories(destDirName, "*REV*", SearchOption.AllDirectories).Where(f => f.StartsWith("REV"));
foreach (var rev in revFolders)
{
Console.WriteLine(int.Parse(rev.Replace("REV", "")));
listOfRevs.Add(int.Parse(rev.Replace("REV", "")));
}
if (listOfRevs.Count > 0)
{
nextRev = listOfRevs.Max();
Console.WriteLine(nextRev);
nextRev++;
}
revFolder = "REV" + nextRev.ToString("000");
Console.WriteLine("New Folder: " + revFolder);
** UPDATE **
Thanks to NetMage the problem was fixed, however I still had a few bugs. Here's the working code:
string revf = "";
int nextRev = 0;
List<int> listOfRevs = new List<int>();
IEnumerable<string> revFolders = Directory.GetDirectories(destDirName, "REV*", SearchOption.AllDirectories);
foreach (var rev in revFolders)
{
if (rev.Contains("REV"))
{
revf = rev.Split('\\').Last();
listOfRevs.Add(int.Parse(revf.Replace("REV", "")));
}
}
if (listOfRevs.Count > 0)
{
nextRev = listOfRevs.Max();
nextRev++;
}
revFolder = "REV" + nextRev.ToString("000");
Change your directory search to
IEnumerable<string> revFolders = Directory.GetDirectories(destDirName, "REV*", SearchOption.AllDirectories);
Based on the results from this, you will to change the maximum finding code - I would use LINQ:
var maxREV = Directory.GetDirectories(destDirName, $"REV*", SearchOption.AllDirectories)
.Select(d => Int32.TryParse(Path.GetFileName(d).Substring(3), out int num) ? num : (int?)null)
.Where(n => n.HasValue)
.Select(n => n.Value)
.Max();
var revFolder = "REV" + (maxREV+1).ToString("000");
Console.WriteLine("New Folder: " + revFolder);
I put in some error handling to skip files that don't have an integer after "REV".
I have a script that is rather simple, it boots up WinSCP and checks the directory for a file that starts with "TSA". If the file exists, it exits, if it does not exist, it transfers over a new file.
Its up and running on my Windows 7 machine, that is where i created it - but when i transfer it over to my server [windows server 2003] it never finds the file.
My script:
var FILEPATH = "../zfinance/TSA";
// Session to connect to
var SESSION = "someplace#somewhere.com";
// Path to winscp.com
var WINSCP = "c:\\program files\\winscp\\winscp.com";
var filesys = WScript.CreateObject("Scripting.FileSystemObject");
var shell = WScript.CreateObject("WScript.Shell");
var logfilepath = filesys.GetSpecialFolder(2) + "\\" + filesys.GetTempName() + ".xml";
var p = FILEPATH.lastIndexOf('/');
var path = FILEPATH.substring(0, p);
var filename = FILEPATH.substring(p + 1);
var exec;
// run winscp to check for file existence
exec = shell.Exec("\"" + WINSCP + "\" /log=\"" + logfilepath + "\"");
exec.StdIn.Write(
"option batch abort\n" +
"open \"" + SESSION + "\"\n" +
"ls \"" + path + "\"\n" +
"exit\n");
// wait until the script finishes
while (exec.Status == 0)
{
WScript.Sleep(100);
WScript.Echo(exec.StdOut.ReadAll());
}
if (exec.ExitCode != 0)
{
WScript.Echo("Error checking for file existence");
WScript.Quit(1);
}
// look for log file
var logfile = filesys.GetFile(logfilepath);
if (logfile == null)
{
WScript.Echo("Cannot find log file");
WScript.Quit(1);
}
// parse XML log file
var doc = new ActiveXObject("MSXML2.DOMDocument");
doc.async = false;
doc.load(logfilepath);
doc.setProperty("SelectionNamespaces",
"xmlns:w='http://winscp.net/schema/session/1.0'");
doc.setProperty("SelectionLanguage", "XPath");
var nodes = doc.selectNodes("//w:file/w:filename[starts-with(#value, '" + filename + "')]");
if (nodes.length > 0)
{
WScript.Echo("File found");
WScript.Quit(0);
}
else
{
WScript.Echo("File not found");
WScript.Quit(1);
}
After much investigation, i think i've found the piece of code that does not function properly:
// parse XML log file
var doc = new ActiveXObject("MSXML2.DOMDocument.6.0");
doc.async = false;
doc.load(logfilepath);
doc.setProperty("SelectionNamespaces",
"xmlns:w='http://winscp.net/schema/session/1.0'");
The only problem is, i have no idea why. The log file at this point should be written over with the xml code, but this does not happen.
Thanks in advance for any help.
And the answer is........... WinSCP on Windows Server 2003 was WAY out of date. So out of date that the log was completely different from one version to the next. Updated and VIOLA! Problem solved. Thanks for your help.
Maybe you need to install MSXML2.DOMDocument.6.0
http://msdn.microsoft.com/en-us/library/windows/desktop/cc507436%28v=vs.85%29.aspx
If you open up regedit and look for "MSXML2.DOMDocument.6.0" does it find it? If so maybe security settings for the script need to be set in order to be able to create an activeX object.
What can you see when you put some stuff in try catch?
try{
//stuff
}catch(e){
WScript.Echo(e.message);
}
I am using an external .txt file to save the incrementing name index for whenever someone "takes a picture" in my app (i.e. image_1.jpg, image_2.jpg, etc...). I am trying to save the data externally so that a user does not overwrite their pictures each time they run the program. However, because of the way that Processing packages its contents for export I cannot both read and write to the same file. It reads the appropriate file located in the apps package contents, however, when it tries to write to that file, it creates a new folder in the same directory as the app itself and writes to a new file with the same name instead.
Essentially, it reads the proper file but refuses to write to it, instead making a copy and writing to that one. The app runs fine but every time you open it and take pictures you overwrite the images you already had.
I have tried naming the "write to" location the explicitly same link as where the exported app stores the data folder inside the package contents (Contents/Resources/Java/data/assets) but this creates a copy of this directory in the same file as the app.
I have also tried excluding the file I am trying to read/write from my data folder when I export the app by changing the read code to ../storage/pictureNumber.txt and then putting this file next to app itself. When I do this the app doesn't launch at all because it is looking in its own data folder for storage and refuses to go outside of itself with ../ . Has anyone had luck both reading from and writing to the same file in an exported processing .app?
Here is the code for the class that is handling the loading and saving of the file:
class Camera {
PImage cameraImage;
int cameraPadding = 10;
int cameraWidth = 60;
int opacity = 0;
int flashDecrementer = 50; //higher number means quicker flash
int pictureName;
Camera() {
String[] pictureIndex = loadStrings("assets/pictureNumber.txt");
pictureName = int(pictureIndex[0]);
cameraImage = loadImage("assets/camera.jpg");
String _pictureName = "" + char(pictureName);
println(pictureName);
}
void display(float mx, float my) {
image(cameraImage, cameraPadding, cameraPadding,
cameraWidth, cameraWidth-cameraWidth/5);
}
boolean isOver(float mx, float my) {
if (mx >= cameraPadding &&
mx <= cameraPadding+cameraWidth &&
my >= cameraPadding &&
my <= cameraPadding+cameraWidth-cameraWidth/5) {
return true;
}
else {
return false;
}
}
void captureImage() {
save("pictures/"+lines.picturePrefix+"_"+pictureName+".jpg");
pictureName++;
String _null = "";
// String _tempPictureName = _null.valueOf(pictureName);
String[] _pictureName = {_null.valueOf(pictureName)};
saveStrings("assets/pictureNumber.txt", _pictureName);
println(_pictureName);
}
void flash() {
fill(255, opacity);
rect(0,0,width,height);
opacity -= flashDecrementer;
if(opacity <= 0) opacity = 0;
}
}
After a lot of searching I found that you have to use savePath() in order to read from a directory outside of the project.jar. The camera class constructor now looks like this:
path = savePath("storage");
println(path);
String[] pictureIndex = loadStrings(path+"/pictureNumber.txt");
pictureName = int(pictureIndex[0]);
cameraImage = loadImage("assets/camera.jpg");
String _pictureName = ""+char(pictureName);
and everything works!
Let say I have the following folders:
New Folder
- New Folder
- New Folder (2)
- New Folder (3)
- New Folder (4)
New Folder (2)
New Folder (3)
New Folder (4)
And a query
from s in Directory.GetDirectories(#"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
select s
The results:
D:\Project\uploads\New Folder
D:\Project\uploads\New Folder (2)
D:\Project\uploads\New Folder (3)
D:\Project\uploads\New Folder (4)
D:\Project\uploads\New Folder\New Folder
D:\Project\uploads\New Folder\New Folder (2)
D:\Project\uploads\New Folder\New Folder (3)
D:\Project\uploads\New Folder\New Folder (4)
Is there anyway to sort the list to the right order? I expected it to be:
D:\Project\uploads\New Folder
D:\Project\uploads\New Folder\New Folder
D:\Project\uploads\New Folder\New Folder (2)
D:\Project\uploads\New Folder\New Folder (3)
D:\Project\uploads\New Folder\New Folder (4)
D:\Project\uploads\New Folder (2)
D:\Project\uploads\New Folder (3)
D:\Project\uploads\New Folder (4)
Any helps would be appreciated!
This wasn't as trivial as I thought. Probably the most sane solution (aside from building the list recursively) is to implement a comparer for this to do the sorting.
class DirectorySorter : IComparer<string>
{
public int Compare(string x, string y)
{
return StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, '\0'),
y.Replace(Path.DirectorySeparatorChar, '\0'));
var xPaths = x.Split(Path.DirectorySeparatorChar);
var yPaths = y.Split(Path.DirectorySeparatorChar);
var minLength = Math.Min(xPaths.Length, yPaths.Length);
for (int i = 0; i < minLength; i++)
{
var ires = xPaths[i].CompareTo(yPaths[i]);
if (ires != 0) return ires;
}
var lres = xPaths.Length.CompareTo(yPaths.Length);
if (lres == 0)
{
return lres;
}
else if (lres < 0)
{
var i = y.LastIndexOf(Path.DirectorySeparatorChar);
return x.Length == i ? lres : -lres;
}
else //if (lres > 0)
{
var i = x.LastIndexOf(Path.DirectorySeparatorChar);
return y.Length == i ? lres : -lres;
}
}
}
(Seeing Steck's answer shows that I was nearly there with what I originally had. Just that I needed to use the Ordinal string comparer. So it turns out it works using that change.)
On the other hand, we could use some properties of the directory structure to simplify this task and not implement a comparer.
var query = Directory
.EnumerateDirectories(#"D:\Project\uploads", "*", SearchOption.AllDirectories)
.OrderBy(name => name.Replace(Path.DirectorySeparatorChar, '\0'), StringComparer.Ordinal);
private class Comparer : IComparer<string>
{
public int Compare(string x, string y)
{
return StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, '\0'),
y.Replace(Path.DirectorySeparatorChar, '\0'));
}
}
and then
var source = Directory.GetDirectories(#"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
var target = source.OrderBy(x => x, new Comparer()).ToArray();
The only thing you need to change about the default ordering is to make sure that the \ character is always treated as the first letter in your alphabet. I don't have an exact answer how to implement this, but:
You can use order by clause if you find a way to replace \ in the string with a character that is smaller than all other characters and use this replaced string as the key.
You can use Array.Sort and implement your string comparer that re-implements string comparison, but encodes this additional rule about the \ character.
With .NET 4.0 try
Directory.EnumerateDirectories(#"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
it might do what you expect. If it doesn't, you can do it explicitly:
Directory.GetDirectories(#"D:\Project\uploads")
.SelectMany(dir => dir.GetDirectories().OrderBy(sub => sub.Name))
Lastly you might do something like:
from s in Directory.GetDirectories(#"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
order by s.Parent.Name, s.Name
select s
from s in Directory.GetDirectories(#"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
let members = s.Name.Split(new [] {Path.SeparatorChar})
order by members[2], s.Name
select s
to get even more control/flexibility. Chose the simplest approach depending on your needs
Thanks for ur comment and answer guys,
I think life'll be much easier with recursive
void Main()
{
string rootFolder = #"D:\Project\uploads";
string[] f = Directory.GetDirectories(rootFolder, "*.*", SearchOption.AllDirectories);
Func<string, string[]> build = null;
build = (p) => {
return (from x in f where Path.GetDirectoryName(x) == p
from y in new string[]{ x }.Union(build(x)) select y).ToArray();
};
f = build(rootFolder).Dump();
}