xReplace

XML based Procedural text Editor by Joe Cincotta ©2008 Joe Cincotta, Pixolüt Industries

xReplace can be used from the Windows application or from a command line version. The command line version has extra features for controlling the script and automation which the Windows version does not. See the xReplaceCmdReadme file for more information.

Overview

xReplace is a procedural text editor. That is, you define a process of manipulation which will be performed for every file which xReplace opens as input and the result of that manipulation will be sent to the output.

Therefore, there are three parts of an xReplace script, input, output and transformations.

One key aspect to understanding how xReplace works is the idea of a pipeline, where each action defined in the transformations happens at a certain point in that pipeline. Generally when a file is read, its content is stored on the main pipeline. OTher pipelines can be created by storing content to a 'label' and then manipulating content using actions which read from and write to that 'label'.

Essentially, the 'main pipeline' is also just a label. If you read from the label called $$MAIN it is the same as not specifying a label and using the main pipeline. You can implicitly create a label and therefore a new pipeline at any time by just using <toLabel> in any action.

 

Hints

Using CDATA Sections

If you want to use free text in the XML when performing replace, use a <![CDATA[ ]]> section... like this:

<match><![CDATA[ This < symbol would kill the XML script normally. ]]></match>

Tag Naming Conventions

All XML tag names are case sensitive and follow the camelCaseConvention...

Reserved Label Names

$$MAIN is the main content pipeline

$$ABS_FILENAME is the absolute filename being read/manipulated

$$REL_FILENAME is the filename being read/manipulated relative to the base path

$$ABS_OUTNAME is the absolute output filename including all naming rules

$$REL_OUTNAME is the output filename relative to the base path including all naming rules


 

Reference

Example XML file structure is below with explanations...

 

Some notes about the documentation:

[required]  this element is required

[optional] this element is optional

[depends] this element depends on another setting or element

true|false means that the setting is either true or false (not both)

 

 


 

<?xml version="1.0" encoding="utf-8" ?>

<project>

There is a single <project> tag for any xReplace XML document.

[Required]

 

<verbose>true|false</verbose>

Detailed information output from whichever client is being used.

 [Optional, default is false.]

 

<comment>This file is a test</comment>

Comment which is output to console when run, useful for many projects which may have similar purposes.

 [Optional, default is none]

 

<plugins>

Use a plug-in command for xReplace. See the example API in the xReplace distribution. Pipeline commands can be loaded at runtime using a plug-in architecture.

[Optional]

<using command=”mycommand”>pixolut.plugins.MyCommand, plugins</using>

The using command should be the full name of the class including namespace and then the name of the assembly in which the class is located.

</plugins>

 

<debug>

There is a single <debug> tag in any project. The debug tag specifies how extra information should be displayed as the system processes files.

<enabled>true|false</enabled>

Enable the debug mode

 

<displayScript>true|false</displayScript>

When output is being displayed by the debugger, should it display the script being debugged

 

<displayContent>true|false</displayContent>

When output is being displayed by the debugger, should it dump all content in all labels to the debug output.

 

<outputToConsole>true|false</outputToConsole>

Also dump the content to the console (standard out).

 

<logFile>filename</logFile>

Log file to append the logging (debug) output to.

 

</debug>

 

<input>

There is a single <input> tag in any project. The input tag specifies how files or directories are read in and handled by xReplace.

 [required]

 

<path>C:\mySourcePath\</path>

The path which is the starting point to read.

 [required]

 

<pattern>*.htm</pattern>

Pattern can be any valid filename or DOS wildcard patterns, for example:

hello.txt

h?llo.txt

h*.txt

*.*

 [required]

 

<recursive>true|false</recursive>

Flags whether to traverse all sub directories from the base path.

 [optional, default is false]

 

<exclude>this,that,other</exclude>

Comma separated list of strings which can not exist in the filename of the files being read by the path/pattern specified.

 [optional, default is none]

 

<include>this,that,other</include>

Comma separated list of strings which must exist in the filename of the files being read by the path/pattern specified.

 [optional, default is all files are included (as per <pattern> specification)]

 

<includeDepth>1,2,3</includeDepth>

Comma separated list of depths in the directory tree relative to the <path> which should include files. (0 is the root)

 [optional, default is all depths are included]

 

<excludeDepth>2,3,4</excludeDepth>

Comma separated list of depths in the directory tree relative to the <path> which should exclude files. (0 is the root)

 [optional, default is none]

 

<ignoreContent>true|false</ignoreContent>

This flag will put the relative filename in to the main content pipeline instead of the content of the file being read. This is useful for a script which doesn't care about the content of the file, rather the directory the file is in.

 [optional, default is false]

 

</input>

 

<output>

There is a single <output> tag in any project. The output tag specifies how file(s) and created by xReplace.

 [required]

 

<path>C:\myOutputPath</path>

The base path to work in creating files. It is ok to use the same path as the input, but beware that if you do not use <renameRules> then the original files will be overwritten.

 [required]

 

<clean>true|false</clean>

This will recursively delete the content of the output folder before beginning the transformation process.

 [optional, default is false]

 

<flatten>true|false</flatten>

Do not use deep filesystems (like the recursive paths being used by the input) - instead, rename the files and put them all in a single directory. The default rename strategy will simply replace any \ character with an _ character. From this, you can then use the <renameRules> to modify the output filenames.

 [optional, default is false]

 

<singleFile>filename.txt</singleFile>

Append all content output to a single file instead of creating new files for each file traversed. If this value is set it will append all content to the filename specified.

 [optional, default is not set]

   

<renameRules>@drop: _dir,@replace:hello:goodbye,@remove: example,@extension:jpg:jpeg</renameRules>

Rename rules is a 'sub language' which can perform some simple manipulation of the output filename.  The expressions are seperated by commas and each expression has parameters which are separated by colons. See the section at the end of the document on the RenameRules specification.

 

Using impossible rename rules will yield fatal errors from the file system - for example, you cannot name a file Hello*.txt.

 [optional, default is not set]

</output>

 

<transformations>

There is a single <transformations> tag in any project. Inside it are all of the transforms which are to be performed on each file found from the input.

 [required]

 

<if startsWith=”blah” endsWith=”blah”>

[optional]

Boolean logic to determine if the transformation script inside the <if> block should be executed.

 

The <if> statement is different to all other statements for two reasons;

1.        It contains attributes in the <if> tag (described below)

2.        It can contain anything that the <transformations> tag can contains – including more <if> statements.

 

<if> tag uses the following attributes:

 

<if label=”blah” …>

label will specify the label to compare with – otherwise it will compare the default pipeline content

 

<if label=”blah” startsWith=”hello”>

This will see if the label called blah starts with ‘hello’

 

The comparison attributes are:

startsWith       starts with specific string

endsWith        ends with specific string

notStartsWith does not start with specific string

notEndsWith   does not end with specific string

match              match a substring

notMatch        does not match the specified substring

equals             exactly equals the string

notEquals        does not exactly equal the string

empty             the label is empty; this can only be set to true or false

</if>

 

<debug message=”blah” />

[optional]

Output debug information (if debugging is enabled) at this point.

 

<print>

Output something, either to the standard output or a file

 [optional]

 

<fromLabel>blah</fromLabel>

use the content of the variable 'blah' as the source of the message being printed

 [optional, otherwise use <message>]

 

<filename>c:\someTextFile.txt</filename>

a text file to append the content being output to.

[optional, otherwise output to standard output (does not affect the content pipeline)]

 

<renameRules>@drop:startsWith=_dir,@replace:match=hello:replaceWith=goodbye,@remove:match=example,@extension:match=jpg:replaceWith=jpeg</renameRules>

Rename rules is a 'sub language' which can perform some simple manipulation of the filename.  The expressions are seperated by commas and each expression has parameters which are separated by colons. See the section at the end of the document on the RenameRules specification.

 

<message>Hello World!</message>

a message which is to be output to the standard output or file

 [optional, otherwise use <label>]

</print>

 

<insert>

Insert content specified before other content in the toLabel

 [optional]

 

<fromLabel>blah</fromLabel>

use the content of the variable 'blah' as the source of the insertion

 [optional, otherwise use <value>]

 

<value>blah</value>

Insert the value 'blah' before the existing content of <toLabel>

 [optional, otherwise use <fromLabel>]

 

<toLabel>blah</toLabel>

Insert the content in to the variable 'blah'

 [optional, otherwise it will be inserted in to the default pipeline]

</insert>

 

<append>

Append content specified after other content in the toLabel

 [optional]

 

<fromLabel>blah</fromLabel>

use the content of the variable 'blah' as the source of the append

 [required]

 

<value>blah</value>

Append the value 'blah' after the existing content of <toLabel>

 [optional, otherwise use <fromLabel>]

 

<toLabel>blah</toLabel>

Append the content in to the variable 'blah'

 [optional, otherwise it will be appended to the default pipeline]

</append>

 

<store>

Save the content in a variable for later use.

 [optional]

 

<toLabel>blah</toLabel>

The name of the label to store the content

 [required]

 

<fromLabel>blah</fromLabel>

use the content of the variable 'blah' as the source of the store

 [required]

 

Note: You can use the <startsWith>, <endsWith>, <skipStart> and <skipEnd> elements to just grab part of the content from the main content pipeline and store that in the specified label

 

<startsWith>blah</startsWith>

Specify a text which the fragment to be used must start with.

 [optional, otherwise, from the beginning]

 

<endsWith>blah</endsWith>

Specify a text which the fragment to be used must end with.

 [optional, otherwise to the end]

 

<skipStart>string</skipStart>

this allows you to find some content based on a pattern and then ignore (trim) the start part of what you found.

 [optional, otherwise from the start]

 

<skipEnd>string</skipEnd>

this allows you to find some content based on a pattern and then ignore (trim) the end part of what you found.

[optional, otherwise to the end]

 

<ignoreCase>true|false</ignoreCase>

this will ignore case sensitivity of the search terms using <startsWith>, <endsWith>, <skipStart>  and <skipEnd> elements.

[optional, otherwise all comparisons will be case sensitive]

 

<filename>c:\someTextFile.txt</filename>

insert the content of the file specified by <filename>

[optional, alternatively use <value>]

 

<renameRules>@drop:startsWith=_dir,@replace:match=hello:replaceWith=goodbye,@remove:match=example,@extension:match=jpg:replaceWith=jpeg</renameRules>

Rename rules is a 'sub language' which can perform some simple manipulation of the filename.  The expressions are seperated by commas and each expression has parameters which are separated by colons. See the section at the end of the document on the RenameRules specification.

 

<value>this is some text to inject</value>

insert the content specified

[optional, alternatively use <filename>]

 

</store>

 

 

<load>

Retrieve the content stored in a variable/pipeline and put it in the main content pipeline

 [optional]

 

<fromLabel>blah</fromLabel>

Get the variable 'blah' and make it the current pipeline content

[required]

 

<toLabel>blah</toLabel>

Get the variable 'blah' and store it in the specified label instead of on the default content pipeline.

[optional]

      

</load>

 

<delete>

Delete a fragment of text from a pipeline

 

<fromLabel>blah</fromLabel>

The name of the label to read the content

 [optional, otherwise use the main content pipeline]

 

<toLabel>blah</toLabel>

The name of the label to store the content

 [optional, otherwise put back in to the main content pipeline]

 

Note: You can use the <startsWith>, <endsWith>, <skipStart> and <skipEnd> elements to just delete part of the content.

 

<startsWith>blah</startsWith>

Specify a text which the fragment to be used must start with.

 [optional, otherwise, from the beginning]

 

<endsWith>blah</endsWith>

Specify a text which the fragment to be used must end with.

 [optional, otherwise to the end]

 

<skipStart>string</skipStart>

this allows you to find some content based on a pattern and then ignore (trim) the start part of what you found.

 [optional, otherwise from the start]

 

<skipEnd>string</skipEnd>

this allows you to find some content based on a pattern and then ignore (trim) the end part of what you found.

[optional, otherwise to the end]

 

<match>blah</match>

An exact string to match in the content.

[optional]

 

<ignoreCase>true|false</ignoreCase>

this will ignore case sensitivity of the search terms using <startsWith>, <endsWith>, <skipStart>  and <skipEnd> elements.  This tag does not work with <match>.

[optional, otherwise all comparisons will be case sensitive]

 

</delete>

   

<replace>

Replace some content with another piece of content

[optional]

 

<fromLabel>blah</fromLabel>

The name of the label to read the content

 [optional, otherwise use the main content pipeline]

 

<toLabel>blah</toLabel>

The name of the label to store the content

 [optional, otherwise put back in to the main content pipeline]

 

Note: You can use the <startsWith>, <endsWith>, <skipStart> and <skipEnd> elements to just grab part of the content to replace

 

<startsWith>blah</startsWith>

Specify a text which the fragment to be used must start with.

 [optional, otherwise, from the beginning]

 

<endsWith>blah</endsWith>

Specify a text which the fragment to be used must end with.

 [optional, otherwise to the end]

 

<skipStart>string</skipStart>

this allows you to find some content based on a pattern and then ignore (trim) the start part of what you found.

 [optional, otherwise from the start]

 

<skipEnd>string</skipEnd>

this allows you to find some content based on a pattern and then ignore (trim) the end part of what you found.

[optional, otherwise to the end]

 

<match>blah</match>

In the context of replace, the <match> tag can do two things, if it is used on its own, it is used to provide an exact match in the content (either from a label or the main pipeline) but if it is used in conjunction with the <startsWith> and <endsWith> tags, it will perform a sub-replace. Think of sub-replace like a nested replace. Once we find a fragment of text (defined by the <startsWith> and <endsWith> tags, we then look for <match> inside that fragment of text and the value of <replaceWith> will be used in that context.

[optional]

 

<matchChars>abcde</matchChars>

MatchChars will operate precisely the same as the <match> tag, however it will iteratively replace the characters specified instead of treating the content of the tag as a single string.

[optional, cannot be used with <match> at the same time]

 

<replaceWith>blah</replaceWith>

A substitute string which will be put in place

[optional, otherwise use the content of <filename> or <replaceWithLabel>]     

 

<replaceWithLabel>blah</replaceWtihLabel>

Use the content of the specified label in as the replacement content

[optional, otherwise use the content of <filename> or <replaceWith>]    

 

<filename>c:\someTextFile.txt</filename>

Use the content of the specified file in as the replacement content

[optional, otherwise use the content of <replaceWithLabel> or <replaceWith>]

 

<renameRules>@drop:startsWith=_dir,@replace:match=hello:replaceWith=goodbye,@remove:match=example,@extension:match=jpg:replaceWith=jpeg</renameRules>

Rename rules is a 'sub language' which can perform some simple manipulation of the filename.  The expressions are seperated by commas and each expression has parameters which are separated by colons. See the section at the end of the document on the RenameRules specification.

 

<ignoreCase>true|false</ignoreCase>

this will ignore case sensitivity of the search terms using <startsWith>, <endsWith>, <skipStart>  and <skipEnd> elements.  This tag does not work with <match>.

[optional, otherwise all comparisons will be case sensitive] 

 

</replace>

   

<exclude>

The exclude command will exclude a file from being processed further or saved if it has a match

[optional]

 

<fromLabel>blah</fromLabel>

[take the content for this operation from variable called 'blah' :optional - otherwise, use the main pipeline content]

 

<startsWith>blah</startsWith>

Specify a text which the fragment to be excluded must start with.

 [optional, otherwise, from the beginning]

 

<endsWith>blah</endsWith>

Specify a text which the fragment to be excluded must end with.

 [optional, otherwise to the end]

 

<match>blah</match>

An exact match in the file to exclude

[optional, otherwise use <startsWith>/<endsWith>]

 

<ignoreCase>true|false</ignoreCase>

this will ignore case sensitivity of the search terms using <startsWith>, <endsWith>, <skipStart>  and <skipEnd> elements.  This tag does not work with <match>.

[optional, otherwise all comparisons will be case sensitive]

 

</exclude>

</transformations>

</project>

 

 


Rename Rules Specification

 

The rename rules are designed to act as a shorthand kind of search and replace for manipulating simple file renaming. As it turns out you can do a lot with this renaming system  - and the plug-ins can also use it via the RenameHelper class.

 

Definitions of each command is below:

 

@drop:text

text is a piece of text which, when matched will be dropped as well as everything else in the filename after that. So, if I had a filename like c:\myOutput\test_file_obe_temp.txt and the command was @drop:obe then the resulting name would be c:\myOutput\test_file_.txt This command does not affect the directory, just the filename.

 

@replace:source_text:replacement_text

This will replace any source text in the filename OR directory and replace it with the replacement text

 

@remove:text

Any text matching text in the filename OR directory will be removed.

 

@extension:source_extension:new_extension

Any filename with the source extension will have its extension renamed to the new extension

 

@insert:text

Insert the text before the filename

 

@append:text

Append the text after the filename

 

To use the : character in a replace expression, use the ^ before it, ^: alternatively you can use a macro ${COLON}

 

Using the value of Labels in a replace rule.

 

Label expansion will not occur in the <output> element renameRules, only for the renameRules in the elements inside the <transformations> element.

 

When you do use renameRules inside transformations, you can use any label you define in the <transform> element as a property in the strings you create. They are accessed just like this: ${label_name}  where label_name is the actual name of the Label.

 

For example if I do this:

 <store>

<toLabel>test</toLabel>

<value>hello</value>

</store>

 

and then use a rename rule, for example in the ZIP, I can access that label like this:

 

<zip>

<renameRules>@drop:chicken,@insert:${test}</renameRules>

</zip>