Generate if file does not exist precondition - acceleo

I would like to test if a file does not exist before generate it.
Is it possible to use something like isFileExist ?
[template public generate(conf : Configuration) ? (isFileExist('/path/to/file.txt'))]
[file ('/path/to/file.txt', false, 'UTF-8')]
file content
[/file]
[/template]
Thanks in advance.

I just had the same problem as you have, and I finally got it working by "externalizing" this function in an external Java class. That way, you can just define a method such as:
public boolean existsFile(String filepath) {
Path p = Paths.get(filepath);
p.normalize();
File f = new File(filepath);
return f.exists();
}
and then call it from Acceleo by defining a query like:
[query public existsFile(filePath : String):
Boolean = invoke('utils.AcceleoUtils', 'existsFile(java.lang.String)', Sequence{filePath})
/]
This way, in your example, you could do
[template public generate(conf : Configuration)]
[if existsFile('/path/to/file.txt')/]
[file ('/path/to/file.txt', false, 'UTF-8')]
file content
[/file]
[/if]
[/template]
p.d: Be carefull with the paths, because the ´file´ tag outputs your files in your target path by default, so in case you´re not providing an absolute Path you will need to include it either when calling or inside the existsFile function.

Related

How to make up-to-date check notice non-file arguments in a Gradle Task?

Suppose I have a task class MyTask which reads an input file, does some conversion on it and writes an output file (build.gradle):
class MyTask extends DefaultTask {
#InputFile File input
#OutputFile File output
String conversion
#TaskAction void generate() {
def content = input.text
switch (conversion) {
case "lower":
content = content.toLowerCase()
break;
case "upper":
content = content.toUpperCase()
break;
}
output.write content
}
}
project.task(type: MyTask, "myTask") {
description = "Convert input to output based on conversion"
input = file('input.txt')
output = file('output.txt')
conversion = "upper"
}
Now this all works fine, when I change input.txt or remove output.txt in the file system it executes again.
The problem is that it doesn't execute if I change "upper" to "lower", which is wrong.
I can't (don't want to) put build.gradle in the inputs list because that would be a hack, also wouldn't work if, say, a command line argument changes what gets into the myTask.conversion field. Another one I thought of is to write the value into a file in a setConversion() method and put that on the inputs list, but again, that's a hack.
How can I make gradle notice the change the official way? Also if you know where can I read more about this, don't hold back on links ;)
I read More about Tasks and Writing Custom Plugins, but it gave me no clue.
There is a handy #Input annotation, which is the same as Opal's answer (check its source code), but little more concise:
#Optional #Input String conversion
Here's a Gradle goodness article about this.
Not sure if it's helpful but I'd try:
class MyTask extends DefaultTask {
#InputFile File input
#OutputFile File output
String conversion
#TaskAction void generate() {
def content = input.text
switch (conversion) {
case "lower":
content = content.toLowerCase()
break;
case "upper":
content = content.toUpperCase()
break;
}
output.write content
}
void setConversion(String conversion) {
this.inputs.property('conversion', conversion)
this.conversion = conversion
}
}
Could you please check it?

How to get a path out of an NSSavePanel object in Swift

I have been attempting to use the NSSavePanel to save text files in a program written in Swift. The only issue is that every time I attempt to use the URL attribute, it has a nil value. Yes, I have a folder selected and a file name in the input when testing. Here's my code:
let saveDialog = NSSavePanel();
saveDialog.beginWithCompletionHandler() { (result: Int) -> Void in
if result == NSFileHandlingPanelOKButton {
let file = NSFileHandle(forWritingToURL: saveDialog.URL!, error: nil)!;
for match in Globals.matches {
if let data = (match.toString() as NSString).dataUsingEncoding(NSUTF8StringEncoding) {
file.writeData(data);
}
}
}
}
// other setup code not shown
When I run this, I always get the Swift equivalent of a null-pointer exception on the
let file = NSFileHandle(forWritingToURL: saveDialog.URL!, error: nil)!;
line. Can I please have some help? What am I doing wrong?
Check the documentation for NSFileHandle(forWritingToURL:error:), it says:
The initialized file handle object or nil if no file exists at url.
So this only works if the file already exists. Which is probably not what you want.
It looks like NSFileHandle cannot create new files at all, so I would just use the following before you try to open the file:
NSFileManager.defaultManager()
.createFileAtPath(saveDialog.URL.path, contents: NSData(), attributes: nil)
Which will create an empty file, which you can then open and write your data to.

Using Routes to correct typos in a URL

I'm managing an MVC3 app where I need to support the ability of 3rd parties to create link to assets within my domain. Because some of the links are sliced and diced by mail merges and other text editing problems, URLs with typos have been introduced, e.g.:
/Content/ima!+ges/email/spacer.gif
or
/Content/image++s/email+/spacer.gif
I'd like to strip these extraneous characters by RegEx before attempting to serve them. I _think this is something a Route method could accomplish and I'd welcome a pointer or two to articles that demonstrate this approach.
ADDENDUM (cuz I need the formatting):
Implementing #Nathan's routing I'm unable to send the filename to the controller handler - it's always seeing a null value passed in. I've tried both 'filepath' and 'path' with the same 'null' result.
routes.MapRoute(
"MangledFilename",
"{*filepath}",
new { controller = "MangledFilename", action = "ServeFile" }
);
I think this is a matter of configuring wildcard handling on IISExpress and am looking for that solution separately. The more serious immediate problem is how your suggestion returns the HttpNotFound - i'm getting a hard IIS exception (execution halts with a YellowScreenDeath) instead of the silent 404 result.
public ActionResult ServeFile(string filePath)
{
if (filePath != null) // workaround the null
{
...
}
return HttpNotFound();
}
thx
I think something along this approach should work:
First add a route like this to the end of your route registering declarations:
routes.MapRoute(
"MangledFilename",
"{*filepath}",
new { controller = "MangledFilename", action = "ServeFile" });
If you haven't seen them before, a route parameter with an * after the opening { is a wildcard parameter, in this case it will match the entire path. You could also write it like content/{*filepath} if you wanted to restrict this behavior to your content directory.
And then a controller something like this should do the trick:
public class MangledFilenameController : Controller
{
public ActionResult ServeFile(string filePath)
{
filePath = CleanFilePath(filePath);
var absolutePath = Server.MapPath(filePath);
if (System.IO.File.Exists(absolutePath))
{
var extension = System.IO.Path.GetExtension(absolutePath);
var contentType = GetContentTypeForExtenion(extension);
return File(absolutePath, contentType);
}
return HttpNotFound();
}
private string CleanFilePath(string filepath)
{
//clean the path up
return filepath;
}
private string GetContentTypeForExtenion(string extension)
{
//you will want code here to map extensions to content types
return "image/gif";
}
}
In regards to mapping an extension to a MIME / content type for the GetContentTypeForExtension method, you could choose to hard code types you are expecting to serve, or use one of the solutions detailed in this post:
File extensions and MIME Types in .NET
EDIT:
After thinking about it, I realized there's another way you can handle the ServeFile action. Redirecting to the existing file could be simpler. I'm leaving the original method I wrote above and adding the alternative one here:
public ActionResult ServeFile(string filePath)
{
filePath = CleanFilePath(filePath);
var absolutePath = Server.MapPath(filePath);
if (System.IO.File.Exists(absolutePath))
{
return RedirectPermanent(filePath);
}
return HttpNotFound();
}
I believe #Nathan Anderson provided a good answer but it seems incomplete.
If you want to correct the typos and the types are as simple as those you mentioned then you can use Nathan code but before trying to find the file, you remove any plus or exclamation point characters in the path and you can do it like this:
String sourcestring = "source string to match with pattern";
String matchpattern = #"[+!]";
String replacementpattern = #"";
Console.WriteLine(Regex.Replace(sourcestring,matchpattern,replacementpattern));
Generated this code from the My Regex Tester tool.
This is the code you need. This code also removes any + character from the filename. If you don't want that behavior, you may select a substring without the filename and only replace + and ! characters before the filename.

Dynamics AX 2009: Batch Trouble with AsciiIO class

I have a custom class, we'll call it FileProcessUpload and it extends RunBaseBatch. It more or less creates a CSV file and then uploads it to an FTP server. When the class is run manually, everything works fine. However, when submitted as a Batch Job, there is an error in the infolog stating "AsciiIO object not initialized".
Probably the most important thing to note here is that this Batch Job is being delegated to a different AOS.
Here is a cropped down version of the offending code:
void CreateFiles()
{
#File
AsciiIO asciiio;
FileIOPermission permission;
ATable aTable;
str outputFile;
str directory;
;
directory = #'C:\Uploads';
ouptutFile = directory + #'\output.csv';
if (!WinAPI::folderExists(directory))
{
WinAPI::createDirectory(directory);
}
// Try to assert the appropriate file access mode
permission = new FileIOPermission(outputFile, #io_write);
permission.assert();
// Try to open the file for writing
asciiio = new AsciiIO(outputFile, #io_write);
if (asciiio != null)
{
while select aTable
{
// Write the necessary lines into the file
asciiio.write(aTable.field1 + ',' + aTable.field2);
}
}
else
{
error('Could not create file: ' + outputFile);
}
// Close file and release permission assertion
asciiio = null;
CodeAccessPermission::revertAssert();
}
Does the service user that Ax is running under have permissions to read/write the file?
You are using the WinAPI class, but should you be using WinAPIServer class instead? You may be executing on the server of course.
Do you need to add to your class the following public boolean runsImpersonated() { return false; } and run this class on a client?
Good luck
Edit: Executing your code via the server static void mainOnServer(Args args) method signature is commonly used (see PurchFormLetter class for it's usage) to make sure that you execute on the server. It is called from static void main(Args args)
Use file path and file name instead of str as directory and name
If runbasebatch then should put pack/uppack filePath and fileName and put it into currentVersion control at classdeclaration.
If you move/delete/encrytion/read file, using system.io.file /system.io.stream, or streamreader, or system.net.ftpwebrequest, and system.net.ftpwebresponse, remember to run on server static void method for this...
Any file format I have done, txt, .csv, .gpg, I move around file easily in/out ax to other server, no problem to just write a file inside of AX by fellowing the above rule..

Is it possible to get the complete file path of a class where the source code is located using coderush APIs?

I wanted to get the full file path when the caret in visual studio is at object Creation or referring a method of some other class.
Something like
Class CurrentClass
{
Class2 object1=new Class2();
object1.method1();
}
Can I get the complete file path like c:\ProjectLocation\Class2.cs.
When I get this line in visual studio.
Class2 object1=new Class2();
You can resolve the active expression (object creation expression, type reference expression, method reference expression), and get the file name with resolved declaration, using code like this:
Expression activeExpression = CodeRush.Source.Active as Expression;
if (activeExpression!= null)
{
IElement declaration = activeExpression.Resolve(new SourceTreeResolver());
if (declaration != null)
{
string fileName = declaration.FirstFile.Name;
// use the fileName...
}
}

Resources