I want to generate below code snippet using Velocity.
Map<String, List<String>> fruitsAndTypesMap = new HashMap<String, List<String>>();
String fruit_Apple = "Apple";
List<String> types_Apple = new ArrayList<String>();
types_Apple.add("KA");
types_Apple.add("WA");
fruitsAndTypesMap.put(fruit_Apple, types_Apple);
String fruit_Orange = "Orange";
List<String> types_Orange = new ArrayList<String>();
types_Orange.add("HO");
types_Orange.add("LO");
fruitsAndTypesMap.put(fruit_Orange, types_Orange);
My template_fruits.vm file is as below.
#if ($fruitsMap.size()>0)
Map<String, List<String>> fruitsAndTypesMap = new HashMap<String,List<String>>();
#foreach( $fruitName in $fruitsMap.keySet() )
String fruit_$fruitName = "$fruitName";
List<String> types_$fruitName = new ArrayList<String>();
#foreach( $fruitType in $fruitsMap.get($fruitName) )
types_$fruitName.add("$fruitType.name");
#end
fruitsAndTypesMap.put(fruit_$fruitName, types_$fruitName);
#end
#end
Issue is with the statement: types_$fruitName.add("$fruitType.name");
it does not evaluate the $fruitName properly.
But if i modify the statement to be like: types_($fruitName).add("$fruitType.name");, it is evaluated properly but the value is surrounded with brackets. I dont understand the brackets trick for evaluation.
Below code can be used to load $fruitsMap
public static Map<String, List<Fruit>> getFruitsMap(){
Map<String, List<Fruit>> fruitsMap = new HashMap<String, List<Fruit>>();
List<Fruit> applesList = new ArrayList<Fruit>();
Fruit fruit_Apple = null;
fruit_Apple = new Fruit(); fruit_Apple.setName("KA");
applesList.add(fruit_Apple);
fruit_Apple = new Fruit(); fruit_Apple.setName("WA");
applesList.add(fruit_Apple);
List<Fruit> orangesList = new ArrayList<Fruit>();
Fruit fuit_Orange = null;
fuit_Orange = new Fruit(); fuit_Orange.setName("HO");
orangesList.add(fuit_Orange);
fuit_Orange = new Fruit(); fuit_Orange.setName("LO");
orangesList.add(fuit_Orange);
fruitsMap.put("Apple", applesList);
fruitsMap.put("Orange", orangesList);
return fruitsMap;
}
Code related to template execution:
VelocityEngine velEngine = new VelocityEngine();
velEngine.init();
Template template = velEngine.getTemplate("template_fruits.vm");
VelocityContext context = new VelocityContext();
context.put("fruitsMap", FruitClient.getFruitsMap());
StringWriter writer = new StringWriter();
template.merge(context, writer);
System.out.println("Content: " + writer.toString());
Any hints of what is the wrong with the statement--> types_$fruitName.add("$fruitType.name"); will be helpful.
Thanks
The statement should be:
types_${fruitName}.add("$fruitType.name");
Otherwise, Velocity tries to call an add method on the $fruitName object.
Related
My ASP.NET Core MVC project has several reports. To render the reports as PDF, I'm using AspNetCore.Reporting library.
This library works fine for a single report but due to some cache issues it throws an exception while generating another report. The solution I found on the internet was to run report generation as a new process but I don't know how to implement that.
I found the suggestion to use Tmds.ExecFunction to run report generation as a seperate process. But I dont know how to pass parameters to the function.
Here is my code:
string ReportName = "invoiceDine";
DataTable dt = new DataTable();
dt = GetInvoiceItems(invoiceFromDb.Id);
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("bParam", $"{invoiceFromDb.Id}");
param.Add("gParam", $"{salesOrderFromDb.Guests}");
param.Add("tParam", $"{invoiceFromDb.Table.Table}");
param.Add("dParam", $"{invoiceFromDb.Time}");
param.Add("totalP", $"{invoiceFromDb.SubTotal}");
param.Add("t1", $"{tax1}");
param.Add("t2", $"{tax2}");
param.Add("tA1", $"{tax1Amount}");
param.Add("tA2", $"{tax2Amount}");
param.Add("AT1", $"{totalAmountWithTax1}");
param.Add("AT2", $"{totalAmountWithTax2}");
param.Add("terminalParam", $"{terminalFromDb.Name}");
param.Add("addressParam", $"{t.Address}");
param.Add("serviceParam", "Service Charges of applicable on table of " + $"{personForServiceCharges}" + " & Above");
var result = reportService.GenerateReport(ReportName, param, "dsInvoiceDine", dt);
return File(result,"application/Pdf");
This is my version of the function:
``` public byte[] GenerateReport(string ReportName, Dictionary<string,string> Parameters,string DataSetName,DataTable DataSource )
{
string guID = Guid.NewGuid().ToString().Replace("-", "");
string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("POS_Website.dll", string.Empty);
string ReportfullPath = Path.Join(fileDirPath, "\\Reports");
string JsonfullPath = Path.Join(fileDirPath,"\\JsonFiles");
string rdlcFilePath = string.Format("{0}\\{1}.rdlc", ReportfullPath, ReportName);
string generatedFilePath = string.Format("{0}\\{1}.pdf", JsonfullPath, guID);
string jsonDataFilePath = string.Format("{0}\\{1}.json", JsonfullPath, guID);
File.WriteAllText(jsonDataFilePath, JsonConvert.SerializeObject(DataSource));
FunctionExecutor.Run((string[] args) =>
{
// 0 : Data file path - jsonDataFilePath
// 1 : Filename - generatedFilePath
// 2 : RDLCPath - rdlcFilePath
ReportResult result;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Encoding.GetEncoding("windows-1252");
LocalReport report = new LocalReport(args[2]);
DataTable dt = JsonConvert.DeserializeObject<DataTable>(File.ReadAllText(args[0]));
report.AddDataSource(args[3], dt);
result = report.Execute(RenderType.Pdf, 1,Parameters);
using (var fs = new FileStream(args[1], FileMode.Create, FileAccess.Write))
{
fs.Write(result.MainStream);
}
}, new string[] {jsonDataFilePath, generatedFilePath, rdlcFilePath, DataSetName });
var memory = new MemoryStream();
using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
{
stream.CopyTo(memory);
}
File.Delete(generatedFilePath);
File.Delete(jsonDataFilePath);
memory.Position = 0;
return memory.ToArray();
}
But it throws exception "Field marshaling is not supported by ExecFunction" on line:
var result = reportService.GenerateReport(ReportName, param, "dsInvoiceDine", dt);
No Need to run report generation as a seperate process. Just Dont Pass extension as 1
in:
var result = localReport.Execute(RenderType.Pdf, 1, param);
The Solution is:
int ext = (int)(DateTime.Now.Ticks >> 10);
var result = localReport.Execute(RenderType.Pdf, ext, param);
I want to generate controller layer code through velocity.
I generate a mapping method:
#ResponseBody
#PostMapping(value = "\\${peacetrue.${moduleName}.urls.add}")
public ${ModuleName}VO add(${ModuleName}Add params) {
logger.info("add record[{}]", params);
return ${moduleName}Service.add(params);
}
and then I got exception:
{DomainName}Controller.java.vm[line 18, column 39]
Was expecting one of:
"[" ...
"|" ...
"}" ...
"}" ...
Then I wrote a unit test:
#Test
public void translate() {
Velocity.init();
Map<String, Object> singletonMap = Collections.singletonMap("foo", "bar");
StringWriter stringWriter = new StringWriter();
Velocity.evaluate(new VelocityContext(singletonMap), stringWriter, "log", "$foo");
Assert.assertEquals("bar", stringWriter.toString());
stringWriter = new StringWriter();
Velocity.evaluate(new VelocityContext(singletonMap), stringWriter, "log", "\\${com.${foo}.name}");
Assert.assertEquals("${com.bar.name}", stringWriter.toString());
}
So what should i do?
You can use the #evaluate() directive like this (at least since v1.7):
#PostMapping("#evaluate("\$peacetrue.${moduleName}.urls.add")")
or (for prior versions) like this:
#PostMapping("#set($d='$')#evaluate("${d}peacetrue.${moduleName}.urls.add")")
Or if the EscapeTool is present in the context :
#PostMapping("#evaluate("${esc.dollar}peacetrue.${moduleName}.urls.add")")
Or if $peacetrue has a standard getter for the module (like .getFoo() or get('foo') as a Map) :
#PostMapping("$peacetrue.get($moduleName).urls.add")
it can be achieved with implementation 'org.apache.velocity.tools:velocity-tools-generic:3.0'
#Test
public void translate() {
VelocityEngine engine = new VelocityEngine();
engine.init();
Map<String, Object> singletonMap = Collections.singletonMap("foo", "bar");
StringWriter stringWriter = new StringWriter();
Velocity.evaluate(new VelocityContext(singletonMap), stringWriter, "log", "$foo");
Assert.assertEquals("bar", stringWriter.toString());
stringWriter = new StringWriter();
ToolManager manager = new ToolManager(true, true);
manager.setVelocityEngine(engine);
manager.configure(getEasyFactoryConfiguration());
ToolContext context = manager.createContext();
context.put("foo","bar");
Velocity.evaluate(context, stringWriter, "log", "${esc.d}{com.${foo}.name}");
Assert.assertEquals("${com.bar.name}", stringWriter.toString());
}
private EasyFactoryConfiguration getEasyFactoryConfiguration() {
EasyFactoryConfiguration config = new EasyFactoryConfiguration();
config.toolbox("application").tool(EscapeTool.class);
return config;
}
The JsonObject addProperty cannot support to add another JsonObject.
The official test shown on below:
#Test
public void shouldConstructTheCorrectUrlWithExtraParam() {
JsonObject body = new JsonObject();
CreateRequest req = new CreateRequest("Defect", body);
req.addParam("foo", "Bar");
Assert.assertEquals(req.toUrl(), "/defect/create.js?foo=Bar&fetch=true");
}
What I need is ???:
public void shouldConstructTheCorrectUrlWithExtraParam() {
JsonObject body = new JsonObject();
body.add("testcase",???)
CreateRequest req = new CreateRequest("testcaseresult", body);
req.addParam("foo", "Bar");
Assert.assertEquals(req.toUrl(), "/defect/create.js?foo=Bar&fetch=true");
}
I did a mistake for adding other JsonObject, it's a ref instead a instance.
Works well code:
public void createTestCaseResult(JsonObject testCaseJsonObject) throws IOException, URISyntaxException {
log.println("createTestCaseResult...");
String testCaseRef = testCaseJsonObject.get("_ref").getAsString();
QueryRequest userRequest = new QueryRequest("user");
userRequest.setFetch(new Fetch("UserName", "Subscription", "DisplayName"));
userRequest.setQueryFilter(new QueryFilter("UserName", "=", "lu.han#technicolor.com"));
QueryResponse userQueryResponse = restApi.query(userRequest);
JsonArray userQueryResults = userQueryResponse.getResults();
JsonElement userQueryElement = userQueryResults.get(0);
JsonObject userQueryObject = userQueryElement.getAsJsonObject();
String userRef = userQueryObject.get("_ref").getAsString();
close();
getRestApi();
Date now = new Date();
String pattern = "yyyy-MM-dd'T'HH:mm:ssZ";
SimpleDateFormat format = new SimpleDateFormat(pattern);
JsonObject newResult = new JsonObject();
newResult.addProperty("Verdict", "Pass");
newResult.addProperty("Build", "2014.01.08.1234567");
newResult.addProperty("Tester", userRef);
newResult.addProperty("Date", format.format(now));
newResult.addProperty("CreationDate", format.format(now));
newResult.addProperty("TestCase", testCaseRef);
newResult.addProperty("Workspace", workspaceRef);
CreateRequest createRequest = new CreateRequest("testcaseresult", newResult);
CreateResponse createResponse = restApi.create(createRequest);
log.println("createTestCaseResult DONEļ¼");
log.println(String.format("Created %s", createResponse.getObject().get("_ref").getAsString()));
}
I am trying to query on both Release and Iteration so I can fill out a drop down list with these various values. I'm not quite sure how to do this, however. What are the members of the object that come back via the query if we are able to do this? (Name, FormattedID, CreationDate, etc). Do we just create a new request of type "Release" and "Iteration" ?
Thanks!
Here is a code that queries on releases based on a project reference. If this project is not in a default workspace of the user that runs the code we either need to hardcode the workspace reference or get it from the project.
class Program
{
static void Main(string[] args)
{
RallyRestApi restApi;
restApi = new RallyRestApi("user#co.com", "TopSecret1984", "https://rally1.rallydev.com", "1.40");
var projectRef = "/project/22222222"; //use your project OID
DynamicJsonObject itemWorkspace = restApi.GetByReference(projectRef, "Workspace");
var workspaceRef = itemWorkspace["Workspace"]["_ref"];
Dictionary<string, string> result = new Dictionary<string, string>();
try
{
Request request = new Request("Release");
request.ProjectScopeDown = false;
request.ProjectScopeUp = false;
request.Workspace = workspaceRef;
request.Fetch = new List<string>()
{
"Name"
};
// request.Query = new Query("Project.ObjectID", Query.Operator.Equals, "22222222"); //also works
request.Query = new Query("Project", Query.Operator.Equals, projectRef);
QueryResult queryResult = restApi.Query(request);
foreach (var r in queryResult.Results)
{
Console.WriteLine("Name: " + r["Name"]);
}
}
catch
{
Console.WriteLine("problem!");
}
}
}
}
After a long time of struggling with this not-so-friendly API, I am finally making progress, but now I've come to a really nasty issue.. I have placed "dir" attributes in various places in my html with the value being "rtl".. but the XMLWorker doesn't seem to respect that at all. Does anyone know of a workaround? Here's my method:
public static void Generate<TModel>(string templateFile, TModel model, string outputFile, IEnumerable<string> fonts)
{
string template = System.IO.File.ReadAllText(templateFile);
string result = Razor.Parse(template, model);
using (var fsOut = new FileStream(outputFile, FileMode.Create, FileAccess.Write))
using (var stringReader = new StringReader(result))
{
var document = new Document();
var pdfWriter = PdfWriter.GetInstance(document, fsOut);
pdfWriter.InitialLeading = 12.5f;
document.Open();
var xmlWorkerHelper = XMLWorkerHelper.GetInstance();
var cssResolver = new StyleAttrCSSResolver();
//cssResolver.AddCss(cssFile);
var xmlWorkerFontProvider = new XMLWorkerFontProvider();
foreach (string font in fonts)
{
xmlWorkerFontProvider.Register(font);
}
var cssAppliers = new CssAppliersImpl(xmlWorkerFontProvider);
var htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.SetTagFactory(Tags.GetHtmlTagProcessorFactory());
PdfWriterPipeline pdfWriterPipeline = new PdfWriterPipeline(document, pdfWriter);
HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, pdfWriterPipeline);
CssResolverPipeline cssResolverPipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
XMLWorker xmlWorker = new XMLWorker(cssResolverPipeline, true);
XMLParser xmlParser = new XMLParser(xmlWorker);
xmlParser.Parse(stringReader);
document.Close();
}
}
I've created a sample to show how to parse and display RTL data using XMLWorker. Download it from here.