I need to increment a number in a source file from an Ant build script. I can use the ReplaceRegExp task to find the number I want to increment, but how do I then increment that number within the replace attribute?
Heres what I've got so far:
<replaceregexp file="${basedir}/src/path/to/MyFile.java"
match="MY_PROPERTY = ([0-9]{1,});"
replace="MY_PROPERTY = \1;"/>
In the replace attribute, how would I do
replace="MY_PROPERTY = (\1 + 1);"
I can't use the buildnumber task to store the value in a file since I'm already using that within the same build target. Is there another ant task that will allow me to increment a property?
You can use something like:
<propertyfile file="${version-file}">
<entry key="revision" type="string" operation="=" value="${revision}" />
<entry key="build" type="int" operation="+" value="1" />
so the ant task is propertyfile.
In ant, you've always got the fallback "script" tag for little cases like this that don't quite fit into the mold. Here's a quick (messy) implementation of the above:
<property name="propertiesFile" location="test-file.txt"/>
<script language="javascript">
regex = /.*MY_PROPERTY = (\d+).*/;
t = java.io.File.createTempFile('test-file', 'txt');
w = new java.io.PrintWriter(t);
f = new java.io.File(propertiesFile);
r = new java.io.BufferedReader(new java.io.FileReader(f));
line = r.readLine();
while (line != null) {
m = regex.exec(line);
if (m) {
val = parseInt(m[1]) + 1;
line = 'MY_PROPERTY = ' + val;
}
w.println(line);
line = r.readLine();
}
r.close();
w.close();
f.delete();
t.renameTo(f);
</script>
Good question, it can be done in perl similar to that, but I think its not possible in ant, .NET and other areas.. If I'm wrong, I'd really like to know, because that's a cool concept that I've used in Perl many times that I could really use in situations like you've mentioned.
Related
How could one parse a xml-like string and convert it a separated list?
I am trying to convert the following string:
<Categories>
<Category Assigned="0">
6 Level
<Category Assigned="1">
6.2 Level
<Category Assigned="0">
6.3 Level
<Category Assigned="0">
6.4 Level
<Category Assigned="1">
6.5 Level
</Category>
</Category>
</Category>
</Category>
</Category>
</Categories>
To a separated list like:
6 Level/6.2 Level/6.3 Level/6.4 Level/6.5 Level, 6 Level/6.2 Level
Robin Mills of exiv2 provided a perl script:
http://dev.exiv2.org/boards/3/topics/1912?r=1923#message-1923
That would need to also parse Assigned="1". How can this be done in C++ to use in digikam, inside dmetadata.cpp with a structure like:
QStringList ntp = tagsPath.replaceInStrings("<Category Assigned="0">", "/");
I don't have enough programming background to figure this out, and haven't found any code sample online that do something similar. I'd also like to include the code in exiv2 itself, so that other applications can benefit.
Working code will be included in digikam: https://bugs.kde.org/show_bug.cgi?id=345220
The code you have linked makes use of Perl's XML::Parser::Expat module, which is a glue layer on top of James Clark's Expat XML parser.
If you want to follow the same route you should write C++ that uses the same library, but it can be clumsy to use as the API is via callbacks that you specify to be called when certain events in the incoming XML stream occur. You can see them in the Perl code, commented process an start-of-element event etc.
Once you have linked to the library, it should be simple to write C code that is equivalent to the Perl in the callbacks — they are only a single line each. Please open a new question if you are having problems with understanding the Perl
Note also that Expat is a non-validating parser, which will let through malformed data without comment
Given that the biggest task is to parse the XML data in the first place, you may prefer a different solution that allows you to build an in-memory document structure from the XML data, and interrogate it using the Document Object Model (DOM). The libxml library allows you to do that, and has its own Perl glue layer in the XML::LibXML module
Maik Qualmann has provided a working patch for digikam!
QString xmlACDSee = getXmpTagString("Xmp.acdsee.categories", false);
if (!xmlACDSee.isEmpty())
{
xmlACDSee.remove("</Categories>");
xmlACDSee.remove("<Categories>");
xmlACDSee.replace("/", "|");
QStringList tagsXml = xmlACDSee.split("<Category Assigned");
int category = 0;
int length;
int count;
foreach(const QString& tags, tagsXml)
{
if (!tags.isEmpty())
{
count = tags.count("<|Category>");
length = tags.length() - (11 * count) - 5;
if (category == 0)
{
tagsPath << tags.mid(5, length);
}
else
{
tagsPath.last().append(QString("/") + tags.mid(5, length));
}
category = category - count + 1;
if (tags.left(5) == QString("=\"1\">") && category > 0)
{
tagsPath << tagsPath.value(tagsPath.size() - count - 1);
}
}
}
if (!tagsPath.isEmpty())
{
return true;
}
}
XML Name space issue revisited:
I am still not able to find a good solution to the problem that the findnode or findvalue does not work when we have xmlns has some value.
The moment I set manually xmlns="", it starts working. At least in my case. Now I need to automate this.
consider this
< root xmlns="something" >
--
---
< /root>
My recommended solution :
dynamically set the value to xmlns=""
and when the work is done automatically we can reset to the original value xmlns="something"
And this seems to be a working solution for my XMLs only but its stll manual.
I need to automate this:
How to do it 2 options:
using Perl regex, or
using proper LibXML setNamespace etc.
Please put your thought in this context.
You register the namespace. The point of XML is not having to kludge around with regexes!
Besides, it's easier: you create an XML::LibXML::XPathContext, register your namespaces, and use its find* calls with your chosen prefixes.
The following example is verbatim from a script of mine to list references in Visual Studio projects:
(...)
# namespace handling, see the XML::LibXML::Node documentation
my $xpc = new XML::LibXML::XPathContext;
$xpc->registerNs( 'msb',
'http://schemas.microsoft.com/developer/msbuild/2003' );
(...)
my $tree; eval { $tree = $parser->parse_file($projfile) };
(...)
my $root = $tree->getDocumentElement;
(...)
foreach my $attr ( find( '//msb:*/#Include', $root ) )
{
(...)
}
(...)
sub find { $xpc->find(#_)->get_nodelist; }
(...)
That's all it takes!
I only have one xmlns attribuite at the top of the XML once only so this works for me.
All I did was first to remove the namespace part i.e. remove the xmlns from my XML file.
NODE : for my $node ($conn->findnodes("//*[name()='root']")) {
my $att = $node->getAttribute('xmlns');
$node->setAttribute('xmlns', "");
last NODE;
}
using last just to make sure i come of the for loop in time.
And then once I am done with the XML parsing I will replace the
<root>
with
<root xmlns="something">
using simple Perl file operation or sed editor.
SearchParam searchParam = new SearchParam();
searchParam.Database = Fields.Database;
searchParam.Language = Fields.Language;
searchParam.TemplateIds = templateID;
if (txtQuery != null && !string.IsNullOrEmpty(txtQuery))
searchParam.FullTextQuery = txtQuery;
QueryRunner runner = new QueryRunner("Web");
IEnumerable<SkinnyItem> items = runner.GetItems(searchParam, sc.Search.QueryOccurance.Should, false, "", true, 0, 500);
I wan to make a search for a field that contain certain GUID, is it possible?
I have tried it, but it returns result of 0 where I'm sure that there will be result.
Am I missing sth or do I need to format the GUID format?
This is the format I used "{xxx-xxx-xxx-xxxx}"
Make sure your index is setup correct to include the fields you are searching for and that you are using the correct query syntax.
See this page on how to configure your index and how to do simple and advanced searches.
Also try a hardcoded query to make sure you are not using wrong values somewhere or wrong formatting.
You're question is unclear so I'm going to interpret here. You say you want to do a full text search for a GUID. Those are naturally two separate things. I assume you have a GUID and want to find all items in the index that have that GUID assigned via some (any?) field. You'll want to use the RelatedIds property of the SearchParam (code here) for this:
Here's some sample code:
var searchParam = new SearchParam
{
Database = Sitecore.Context.Database.Name,
Language = Sitecore.Context.Language.Name,
RelatedIds = {YOUR GUID HERE}
};
using (var runner = new QueryRunner(indexName))
{
return runner.GetItems(searchParam);
}
Also, if you have more than one GUID, you can pipe-delimit them, e.g. RelatedIds = {GUID1}|{GUID2}
Have a look at what is in the index, you can check the field values stored using Sitecore Rocks ->
Right Click Your site, Manage, Indexes, {Your Index}.
Check one of the guid fields to see whats in there.
I think they are stored without the curly brackets, hyphens and in lower case, you'd just need to format the guid before passing it in.
If you want to search on field value then you should use the FieldSearchParam instead of SearchParam. Here is the sample code:
var searchParam = new FieldSearchParam
{
Database = Sitecore.Context.Database.Name,
Language = Sitecore.Context.Language.Name,
TemplateIds = DealerTemplateId,
FieldName = <FieldName>,
FieldValue = <FieldValue>
};
QueryRunner runner = new QueryRunner("Web");
IEnumerable<SkinnyItem> items = runner.GetItems(searchParam);
This FieldSearchParam, is extended from SearchParam in "Sitecore.SharedSource.Searcher" library. This in open source, you can either add reference or get code in your project.
One thing to note here is the format of FieldValue, your field value should all be in small and without "-", so if your GUID is "{A9294EF5-0E36-4616-A77E-B7B100057EBA}" then you should pass ""a9294ef50e364616a77eb7b100057eba" i.e. no "{}" and all characters should be in small.
Try this, as this worked for me.
Note: Please ensure you have checked the configuration as per suggestion from #martijn
What works for me is this:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<databases>
<database id="master" singleInstance="true" type="Sitecore.Data.Database, Sitecore.Kernel">
<Engines.HistoryEngine.Storage>
<obj type="Sitecore.Data.$(database).$(database)HistoryStorage, Sitecore.Kernel">
<param connectionStringName="$(id)" />
<EntryLifeTime>30.00:00:00</EntryLifeTime>
</obj>
</Engines.HistoryEngine.Storage>
<Engines.HistoryEngine.SaveDotNetCallStack>false</Engines.HistoryEngine.SaveDotNetCallStack>
</database>
</databases>
<search>
<configuration>
<indexes>
<index id="newsArticles" type="Sitecore.Search.Index, Sitecore.Kernel">
<param desc="name">$(id)</param>
<param desc="folder">newsArticle</param>
<Analyzer ref="search/analyzer" />
<locations hint="list:AddCrawler">
<master type="Sitecore.SharedSource.SearchCrawler.Crawlers.AdvancedDatabaseCrawler,Sitecore.SharedSource.SearchCrawler">
<Database>master</Database>
<Root>/sitecore/content/Data</Root>
<IndexAllFields>true</IndexAllFields>
<MonitorChanges>true</MonitorChanges>
<include hint="list:IncludeTemplate">
<News>{EF11A8D0-D373-4A4B-90BA-16984D277612}</News>
</include>
Now I have included the template id which I need my search index to search. When I build I can see that the items containing this specific template id exists.
Now to search:
try
{
List<Item> items = new List<Item>();
Sitecore.Search.Index indx = SearchManager.GetIndex("newsArticles");
using (IndexSearchContext searchContext = indx.CreateSearchContext())
{
var db = Sitecore.Context.Database;
CombinedQuery query = new CombinedQuery();
QueryBase catQuery = new FieldQuery("category", GuidsToSearchFor); //FieldName, FieldValue.
SearchHits results = searchContext.Search(catQuery); //Searching the content items by fields.
SearchResultCollection result = results.FetchResults(0, numOfArticles);
RetrieveUrlListings(myItems, db, result, true);
}
}
Your result will contain the list of the items present in the index viewer.
Hope this helps! Check out this link for more.
I need to come up with a program that generates an xml file like this:
<?xml version="1.0"?>
<xc:XmlCache xmlns:xc="XmlCache" xc:action="Update">
<xc:XmlCacheArea xc:target="AllSubFields" xc:value="MarketParameters">
<mp:nickName xmlns:mp="mx.MarketParameters" xc:value="MDS">
<mp:date xc:value="TODAY">
<fx:forex xmlns:fx="mx.MarketParameters.Forex">
<fxsp:spot xmlns:fxsp="mx.MarketParameters.Forex.Spot">
<fxsp:pair type="Fields" value="USD/BRL">
<mp:ask xc:type="Field" xc:keyFormat="N">1.890</mp:ask>
<mp:bid xc:type="Field" xc:keyFormat="N">1.800</mp:bid>
</fxsp:pair>
</fxsp:spot>
</fx:forex>
</mp:date>
</mp:nickName>
</xc:XmlCacheArea>
</xc:XmlCache>
with the values in the nodes mp:ask and mp:bid randomly generated but between two predefined values (1.65 and 1.99).
After the xml is generated in the same directory of the program, the program should run a command in the cmd command line that states:
cachetool.bat -i cacheBody.xml -u REALTIME
where cachetool.bat is an already done bash script that cannot be changed and that is also place in the same directory of the program, and where cacheBody.xml is that previously generated xml.
The trick here is that this should run repeatedly overwriting the xml file with new values each time and then running the command again calling the xml with the new values.
There should be a way to easy interrupt the loop, but besides that, this should run indefinitely.
Note: there isn't a strict rule to use c or c++, if it isn't feasible in these languages or if there other ways to do it easily, please feel free to suggest. My initial proposal is in these languages because these are the two that I'm a little used to deal with.
I'm learning how to use javascript for Windows local scripting, so here's a solution in javascript.
It looks like you don't really need to generate the XML dynamically, but rather the XML structure is static and only a couple data fields are dynamic. With that in mind, I approached the problem with search-and-replace using a template file.
The template file (template.xml) contains xml content with some variables to search and replace. The format variable format is $RANDOM_X_Y$, where X and Y are the lower and upper bounds for the random number. To help the example, I generated the ask and bid prices slightly differently in the template file:
<?xml version="1.0"?>
<xc:XmlCache xmlns:xc="XmlCache" xc:action="Update">
<xc:XmlCacheArea xc:target="AllSubFields" xc:value="MarketParameters">
<mp:nickName xmlns:mp="mx.MarketParameters" xc:value="MDS">
<mp:date xc:value="TODAY">
<fx:forex xmlns:fx="mx.MarketParameters.Forex">
<fxsp:spot xmlns:fxsp="mx.MarketParameters.Forex.Spot">
<fxsp:pair type="Fields" value="USD/BRL">
<mp:ask xc:type="Field" xc:keyFormat="N">1.$RANDOM_65_99$0</mp:ask>
<mp:bid xc:type="Field" xc:keyFormat="N">1.$RANDOM_650_990$</mp:bid>
</fxsp:pair>
</fxsp:spot>
</fx:forex>
</mp:date>
</mp:nickName>
</xc:XmlCacheArea>
</xc:XmlCache>
The javascript file is called replace.js. All versions of Windows should be able to execute it natively without installing any extra components.
if( WScript.Arguments.Count() != 2 || WScript.Arguments.Item(0) == WScript.Arguments.Item(1) )
{
WScript.Echo("Usage: replace.js <template> <output filename>");
WScript.Quit();
}
var template_filename = WScript.Arguments.Item(0);
var output_filename = WScript.Arguments.Item(1);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var ForReading = 1;
var file, file_contents, lower, upper;
var var_regex = /\$RANDOM_(\d+)_(\d+)\$/g;
if( fso.FileExists(template_filename) )
{
file = fso.OpenTextFile(template_filename, ForReading, false);
file_contents = file.ReadAll().replace(var_regex,
function(str, lower, upper) {
return Math.floor(
Math.random() * (+upper - +lower + 1)) + +lower;
});
file.Close();
file = fso.CreateTextFile(output_filename, true);
file.Write(file_contents);
file.Close();
}
else
{
WScript.Echo("Template does not exist: " + template_filename);
}
Now to run your scripts indefinitely, just create a batch file called run.bat or whatever and have it run the javascript and batch files in a loop. CTRL-C will exit the script.
#echo off
echo Starting. Press CTRL-C to exit.
:loop
replace.js template.xml cacheBody.xml
cachetool.bat -i cacheBody.xml -u REALTIME
goto loop
Well, to create the random value, you can use the rand() function, and just scale it so it's between the two values you want.
To call the command line, try system("cachetool.bat -i cacheBody.xml -u REALTIME");
And for the xml, if it's all the same except for the numbers, you can just hardcode it. If not, you'll need an xml library.
I have an xml file of the form:
<property name="foo" value="this is a long value">stuff</property>
There are many properties but I want to match the one with name foo and then replace its value attribute with something else as so:
<property name="foo" value="yet another long value">stuff</property>
I was thinking to write a regular expression to match everything after "foo" to the end of the tag ( ">" ) and replace that, but I can't seem to get the syntax right.
I'm trying to do this using sed, if that's any help.
/property name=\"foo\" value=\"([^\"]*)\"/
Then just replace the first submatch with the new value of your wishing.
You probably don't want to use a regex for manipulating an xml file. Please instead consider xslt, which is aware of xml rules and won't cause your transformed document to become malformed.
If you're doing this in the context of a browser, you could create a throwaway DOM node containing the XML and just walk that to replace attribute values.
This function will call a callback on every child node:
const walkDOM = (node, callback) => {
callback(node);
[...node.children].forEach(child => {
walkDOM(child, callback)
});
}
You can then use this to update any attributes matching conditions you'd like (here replacing any, assuming you have an XML string called svgXml:
const containerEl = document.createElement('div');
containerEl.innerHTML = svgXml;
walkDOM(containerEl, el => {
const attributes = [...el.attributes];
attributes.forEach(attr => {
if (attr.name === 'foo' && attr.value === 'this is a long value']) {
attr.value = 'yet another long value';
}
});
});
const outputSvgXml = containerEl.innerHTML;
Of course you could further optimize this by using querySelectorAll(property) to only walk <property> nodes, etc.
I found this useful for updating an SVG while taking advantage of the browser's robustness.