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>