How can I use global variables in a TT file?
If I declare a variable in the header I get a compile error if I reference it in a function.
<## template debug="false" hostspecific="false" language="C#" #>
<## output extension=".cs" #>
<#
int ValueForThisFile = 35;
SomeFunction();
#>
<#+
void SomeFunction() {
#>
public void GeneratedCode() {
int value = <#=ValueForThisFile#>;
}
<#+
}
#>
I know that I could pass it as an argument but there are hundreds of calls and it would be syntactically tighter if I could avoid that. If this were one file I could hard code the value but there are dozens of files that have different settings and common include files that generate the code.
I don't think that is possible. When T4 parses your template it is actually generating a class. All the <# #> contents are injected into a single method on that class while all <#+ #> tags are added as methods to that class, allowing you to call them from the single method <# #> tags. So the scope of the "ValueForThisFile" variable is limited to that single method. For a simple example, this template:
<## template debug="false" hostspecific="false" language="C#" #>
<## output extension=".cs" #>
<#
int ValueForThisFile = 35;
SomeFunction();
#>
<#+
void SomeFunction() {
return ValueForThisFile;
}
#>
Would Generate a class like this:
class T4Gen {
private void MainWork() {
int ValueForThisFile = 35;
this.SomeFunction();
}
private void SomeFunction{
return ValueForThisFile;
}
}
The variable "ValueForThisFile" is only scoped to the MainWork function. The actual class T4 generates is much more complicated but as you see there would be no way to have a global variable in code like that.
Structuring your T4 script like this might help, I have been using similar approaches in my projects successfully: -
<## template debug="false" hostspecific="false" language="C#" #>
<## output extension=".cs" #>
<#
var Context = new ScriptContext();
Context.SomeFunction();
#>
// This file is generated by Build/Info.tt, do not modify!
void SomeFunction() {
public void GeneratedCode() {
int value = <#=Context.ValueForThisFile#>;
}
}
<#+
public class ScriptContext {
public int ValueForThisFile = 35;
public void SomeFunction()
{
ValueForThisFile = 42;
}
}
#>
Its possible to share variable accross functions in T4 template, Try this,
<## template debug="true" hostSpecific="true" #>
<## output extension=".cs" #>
<## import namespace="System.Collections.Generic" #>
<#
InitGlobalVariable();
AddNames();
ShowNames();
#>
<#+
//T4 Shared variables
List<string> names;
#>
<#+
private void InitGlobalVariable()
{
names = new List<string>();
}
private void AddNames()
{
names.Add("Mickey");
names.Add("Arthur");
}
private void ShowNames()
{
foreach(var name in names)
{
#>
<#= name #>
<#+
}
}
#>
Declare your variable inside <#+ ... #> and then initialize inside <# ... #>
Related
What I need
We package our products with Gradle and shadowJar. Some of the libraries we use, utilize individual sections in Jar Manifests, specifically attributes like Implementation-Title and
Implementation-Version. These sometimes show in (the outputs of) our products, so I'd like them to survive the shawdowJar-Process.
Example
lib1.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/lib
...
Implementation-Title: someLib
Implementation-Version: 2.3
...
lib2.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/other/lib
...
Implementation-Title: someOtherLib
Implementation-Version: 5.7-RC
...
=>
product.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/lib
...
Implementation-Title: someLib
Implementation-Version: 2.3
...
Name: org/some/other/lib
...
Implementation-Title: someOtherLib
Implementation-Version: 5.7-RC
...
What I found out
It is rather easy to manipulate the resulting Manifest with shadowJar:
project.shadowJar {
manifest {
attributes(["Implementation-Title" : "someLib"], "org/some/lib")
attributes(["Implementation-Title" : "someOtherLib"], "org/some/other/lib")
}
}
generates exactly what I want, statically.
shadowJar can provide me with a list of dependencies. However, when I iterate over the FileCollection like this
project.shadowJar {
manifest {
for (dependency in includedDependencies) {
// read in jar file and set attributes
}
}
}
Gradle is not happy: "Cannot change dependencies of dependency configuration ':project:products:<ProductName>:compile' after it has been included in dependency resolution."
When I define a new task
def dependencies = [];
project.tasks.register('resolveDependencies') {
doFirst {
gradleProject.configurations.compile.resolvedConfiguration.resolvedArtifacts.each {
dependencies.add(it.file)
}
}
}
project.tasks['shadowJar'].dependsOn(project.tasks['resolveDependencies']);
project.shadowJar {
manifest {
// dependencies will be empty when this code is called
for (dependency in dependencies) {
// read in jar file and set attributes
}
}
}
The dependencies are not resolved in time.
What I'd like to know
How can I access the dependencies without upsetting Gradle? Alternatively, is there another way to merge the named individual sections with shadowJar?
According to https://github.com/johnrengelman/shadow/issues/369 the Transformer interface of shadowJar should be used to do this.
So here comes:
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer;
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext;
import java.io.ByteArrayOutputStream;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.Map.Entry;
import shadow.org.apache.tools.zip.ZipOutputStream;
import shadow.org.apache.tools.zip.ZipEntry;
import shadow.org.codehaus.plexus.util.IOUtil;
import org.gradle.api.file.FileTreeElement;
import static java.nio.charset.StandardCharsets.*
import static java.util.jar.JarFile.*;
/**ManifestVersionMergeTransformer appends all version information sections from manifest files to the resulting manifest file.
* #author Robert Lichtenberger
*/
public class ManifestMergeTransformer implements Transformer {
String includePackages; // regular expression that must match a given package
String excludePackages; // regular expression that must not match a given package
private Manifest manifest;
#Override
public boolean canTransformResource(FileTreeElement element) {
MANIFEST_NAME.equalsIgnoreCase(element.relativePath.pathString);
}
#Override
public void transform(TransformerContext context) {
if (manifest == null) {
manifest = new Manifest(context.is);
} else {
Manifest toMerge = new Manifest(context.is);
for (Entry<String, Attributes> entry : toMerge.getEntries().entrySet()) {
if (mustInclude(entry.getKey())) {
manifest.getEntries().put(entry.getKey(), entry.getValue());
}
}
}
IOUtil.close(context.is);
}
private boolean mustInclude(String packageName) {
return (includePackages == null || packageName.matches(includePackages)) && (excludePackages == null || !packageName.matches(excludePackages));
}
#Override
public boolean hasTransformedResource() {
return true;
}
#Override
public void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {
ZipEntry entry = new ZipEntry(MANIFEST_NAME);
entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time);
os.putNextEntry(entry);
if (manifest != null) {
ByteArrayOutputStream manifestContents = new ByteArrayOutputStream();
manifest.write(manifestContents);
os.write(manifestContents.toByteArray());
}
}
}
I'm trying to pass our application that was using the old Prism 4.0 to latest Prism 7.1.0.431
I'm almost done, everything compiles. Dependency injection has been updated to use latest Unity. So everything seems back on track as I see injection working somewhat.
Though I still have a problem with Module loading: region manager cannot be resolved. I think I'm missing something in my initialization code but cannot find any relevant documentation on that. Try to get into all Prism.Wpf samples but could find relevant code.
Injecting the region manager within module is probably not a good practice from the code I'm seeing while search an answer to my issue, but bear with me that right now it's a huge application and would want to avoid changing that as much as possible:
Here is the exception error I'm having:
EXCEPTION: Prism.Modularity.ModuleInitializeException: An exception occurred while initializing module 'AdvancedExportModule'.
- The exception message was: Resolution of the dependency failed, type = 'Codex.Modules.AdvancedExport.AdvancedExportModule', name = '(none)'.
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, Prism.Regions.IRegionManager, is an interface and cannot be constructed. Are you missing a type mapping?
Am I missing something initialization code for the RegionManager to be mapped and injected correctly by Unity?
Here are the code sample, I tried to simply the most of it and hopefully it's enough for you to understand what's wrong...
This my App.xaml:
<prism:PrismApplication x:Class="Codex.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>
<ResourceDictionary Source="Resources/Merged.xaml"/>
</Application.Resources>
</prism:PrismApplication>
And in my Code behind App.xaml.cs
namespace MyNamespace
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Threading;
using Prism.Ioc;
using Prism.Logging;
using Prism.Modularity;
using Prism.Unity;
public partial class App : PrismApplication
{
private static ILoggerFacade Logger { get; set; }
public static void Main()
{
var application = new App();
application.InitializeComponent();
application.Run();
}
protected override void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
var modulesFilePaths = new Dictionary<string, string>();
modulesFilePaths.Add("Namespace.Modules.Module1.dll", "Namespace.Modules.AdvancedExport.Module1Module");
var pathToExecutingLibrary = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName;
foreach (KeyValuePair<string, string> moduleFilePath in modulesFilePaths)
{
var referenceUri = Path.Combine(pathToExecutingLibrary, moduleFilePath.Key);
var assembly = Assembly.LoadFrom(referenceUri);
var type = assembly.GetType(moduleFilePath.Value);
moduleCatalog.AddModule(
new ModuleInfo(type)
{
ModuleName = type.Name,
Ref = referenceUri,
InitializationMode = InitializationMode.WhenAvailable
});
}
moduleCatalog.Initialize();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
ConfigureViewModelLocator();
var containerExtension = CreateContainerExtension();
containerRegistry.RegisterInstance(containerExtension);
// These methods have been commented out they are use to register all the types of the application.
//RegisterSettings(containerRegistry);
//RegisterServices(containerRegistry);
//RegisterHandlers(containerRegistry);
//RegisterWrappers(containerRegistry);
containerRegistry.RegisterInstance(Dispatcher.CurrentDispatcher);
}
protected override Window CreateShell()
{
Window mainShell = Container.Resolve<MainShell>();
return mainShell;
}
}
}
You're doing too much and the wrong things in your overrides. Example: RegisterTypes should just register types...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// this has already been called by the base class: ConfigureViewModelLocator();
// this has also been called by the base class: var containerExtension = CreateContainerExtension();
// containerRegistry.RegisterInstance(containerExtension);
// These methods have been commented out they are use to register all the types of the application.
//RegisterSettings(containerRegistry);
//RegisterServices(containerRegistry);
//RegisterHandlers(containerRegistry);
//RegisterWrappers(containerRegistry);
containerRegistry.RegisterInstance(Dispatcher.CurrentDispatcher);
}
You should review the source code to get an understanding of how the overrides are supposed to be called. Essentially, they should not call each other, just do their own work.
I would like to extract specific information from 108 Xml files. The general source is also a XML-File with further URLs as resources.
XML-Source
The static method getURL() extracts the URLs in order to set them as URL-paths within a for loop in the main method. The programm works, but it takes approx. 5 minutes to get the data from all files. Any ideas how to increase the performance?
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
public class XmlReader2 {
public static void main(String[] args) throws IOException {
for (int i = 0; i < getURL().size(); i++) {
URL url = new URL(getURL().get(i));
try {
Document doc = new SAXBuilder().build(url);
final String getDeath = String
.format("//ns:teiHeader/ns:profileDesc/ns:particDesc/ns:listPerson/ns:person/ns:death");
XPathExpression<Element> xpath = XPathFactory.instance().compile(getDeath, Filters.element(), null,
Namespace.getNamespace("ns", "http://www.tei-c.org/ns/1.0"));
String test;
for (Element elem : xpath.evaluate(doc)) {
test = elem.getValue();
if (elem.getAttributes().size() != 0) {
test = elem.getAttributes().get(0).getValue();
}
System.out.println(elem.getName() + ": " + test);
}
} catch (org.jdom2.JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static List<String> getURL() throws IOException {
List<String> urlList = new ArrayList<>();
URL urlSource = new URL("http://www.steinheim-institut.de:80/cgi-bin/epidat?info=resources-mz1");
try {
Document doc = new SAXBuilder().build(urlSource);
final String getURL = String.format("/collection");
XPathExpression<Element> xpath = XPathFactory.instance().compile(getURL, Filters.element());
int i = 0;
for (Element elem : xpath.evaluate(doc)) {
while (i != elem.getChildren().size()) {
String url = elem.getChildren().get(i).getAttributes().get(1).getValue();
// System.out.println(url);
urlList.add(url);
i++;
}
}
} catch (org.jdom2.JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return urlList;
}
}
A delay of this order may be caused by fetching files from the web. Find a tool for monitoring HTTP requests issued from your machine to see what is going on. Look in particular for requests for common W3C files such as the XHTML DTD: because these files are requested so often, W3C deliberately injects a delay into the process to encourage people to use local copies of the files. If it turns out that this is the problem, there are various techniques you can use to access cached local copies.
Having said that, I'm puzzled by the logic of your code. The method getURL() appears to fetch and parse the document at http://www.steinheim-institut.de:80/cgi-bin/epidat?info=resources-mz1 every time it is called, and yet you are calling it within a loop, even using getURL().size() as your terminating condition.
I am new to "smooks and freemarker".I want access elements in xml document.I am getting this exception while acessing xml element.I am sending my code.
Exception:-
------------
For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), but this evaluated to a sequence+hash (wrapper: f.e.dom.NodeListModel):
==> employee["first_name"] [in template "free-marker-template" at line 1, column 84]
----
Tip: This XML query result can't be used as string because for that it had to contain exactly 1 XML node, but it contains 0 nodes. That is, the constructing XML query has found no matches.
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${employee["first_name"]} [in template "free-marker-template" at line 1, column 82]
----
Java stack trace (for programmers):
----
freemarker.core.NonStringException: [... Exception message was already printed; see it above ...]
at freemarker.core.EvalUtil.coerceModelToString(EvalUtil.java:381)
at freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
at freemarker.core.DollarVariable.accept(DollarVariable.java:40)
at freemarker.core.Environment.visit(Environment.java:312)
The following are versions
java version : "1.7.0_45"
freemarker : 2.3.22
smooks : 1.6
javacode:-
-----------
package test;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.milyn.Smooks;
import org.milyn.SmooksException;
import org.milyn.container.ExecutionContext;
import org.milyn.event.report.HtmlReportGenerator;
import org.milyn.io.StreamUtils;
import org.milyn.payload.StringResult;
import org.xml.sax.SAXException;
public class SmooksExample {
public SmooksExample() {
// TODO Auto-generated constructor stub
}
protected static void runSmooksNew() throws IOException, SAXException, SmooksException {
try {
Long stTime = System.currentTimeMillis();
System.out.println(stTime + "==" + new Date());
Writer xmlResultWriter = new BufferedWriter(new FileWriter(new File("C:\\Files\\SmookExample\\output_sax.dat")));
transCustomerCSV(new File("C:\\Files\\emp_namespace.xml"), xmlResultWriter);
Long edTime = System.currentTimeMillis();
System.out.println(edTime + "==" + (edTime-stTime) + "===="+ new Date());
String times = String.format("%d min, %d sec",
TimeUnit.MILLISECONDS.toMinutes(edTime-stTime),
TimeUnit.MILLISECONDS.toSeconds(edTime-stTime) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(edTime-stTime))
);
System.out.println(times);
} finally {
//smooks.close();
}
}
public static void transCustomerCSV(File csvSourceReader, Writer xmlResultWriter) throws IOException, SAXException {
File f = new File("C:/TEMPLATES/smooks-config.xml");
URI u = f.toURI();
Smooks smooks = new Smooks(u.getPath());
ExecutionContext ec = smooks.createExecutionContext();
smooks.filterSource(ec, new StreamSource(csvSourceReader), new StreamResult(xmlResultWriter));
}
/**
* #param args
*/
public static void main(String[] args) throws IOException, SAXException, SmooksException {
System.out.println("\n\n");
System.out.println("==============Message In==============");
SmooksExample.runSmooksNew();
System.out.println("======================================\n");
}
}
Smooks-config.xml
--------------------
<smooks-resource-list xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd" xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd" xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd" xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">
<params>
<param name="stream.filter.type">SAX</param>
<param name="default.serialization.on">false</param>
</params>
<core:namespaces>
<core:namespace prefix="empl" uri="http://www.example.com/employees"/>
</core:namespaces>
<resource-config selector="employee,first_name">
<resource>org.milyn.delivery.DomModelCreator</resource>
</resource-config>
<ftl:freemarker applyOnElement="first_name">
<ftl:template><!--<#ftl ns_prefixes={"empl":"http://www.example.com/employees"}>${employee["#id"]},${employee["first_name"]}-->
</ftl:template>
</ftl:freemarker>
</smooks-resource-list>
sample data xml file name: emp_namespace.xml:-
--------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<empl:employees xmlns:empl="http://www.example.com/employees">
<empl:employee id="1">
<empl:first_name>Bill</empl:first_name>
<empl:last_name>Adams</empl:last_name>
<empl:age>25</empl:age>
<empl:hire_date>12-06-1995</empl:hire_date>
<empl:title>Java programmer</empl:title>
<empl:DateCreated>
<empl:Year>1980</empl:Year>
<empl:Month>01</empl:Month>
<empl:Day>01</empl:Day>
</empl:DateCreated>
<empl:DateCompleted>
<empl:Year>1981</empl:Year>
<empl:Month>02</empl:Month>
<empl:Day>02</empl:Day>
</empl:DateCompleted>
</empl:employee>
<empl:employee id="2">
<empl:first_name>Mary</empl:first_name>
<empl:last_name>Jones</empl:last_name>
<empl:age>32</empl:age>
<empl:hire_date>22-09-2001</empl:hire_date>
<empl:title>Sales manager</empl:title>
<empl:DateCreated>
<empl:Year>1982</empl:Year>
<empl:Month>03</empl:Month>
<empl:Day>03</empl:Day>
</empl:DateCreated>
<empl:DateCompleted>
<empl:Year>1983</empl:Year>
<empl:Month>04</empl:Month>
<empl:Day>04</empl:Day>
</empl:DateCompleted>
</empl:employee>
</empl:employees>
In ns_prefixes you specify the namespace prefixes used in the FTL, which is independent of the namespace prefixes used in the XML. FreeMarker doesn't care what the actual prefix was in the XML, it only cares about the namespace URL. So since you have declared the empl prefix, you had to use it the FTL too (like empl\:employee.empl\:first_name - kind of awkward, as the : has to be escaped). But since you mostly access that single namespace, I recommend declaring that namespace URL to be the default:
<#ftl ns_prefixes={"D": "http://www.example.com/employees"}>
And then you don't have to add a prefix:
${employee.first_name}
(Note that here I assume that in Smooks the data-model root is the document element. I don't know if that's true, but anyway, as far as XML namespaces are concerned, this is how it works. Also, you have multiple employee-s in the XML so that above won't work because of multiple matches, but that's another topic.)
This basic code succeeds at making the command scopeA:test accessible in the shell:
package com.A;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.ServiceProperty;
import org.apache.felix.service.command.Descriptor;
#Component(immediate = true)
#Instantiate
#Provides(specifications = Commands.class)
public final class Commands {
#ServiceProperty(name = "osgi.command.scope", value = "scopeA")
String scope;
#ServiceProperty(name = "osgi.command.function", value = "{}")
String[] function = new String[] {
"test"
};
#Descriptor("Example")
public void test() {
System.out.println("hello");
}
}
However, if I add a constructor that depends on another OSGI component, it the command is no longer accessible and "help" doesn't list it. Yet the bundle can still be loading into an active state.
package com.A;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.ServiceProperty;
import org.apache.felix.service.command.Descriptor;
import com.B;
#Component(immediate = true)
#Instantiate
#Provides(specifications = Commands.class)
public final class Commands {
public Commands(#Requires B b) {
}
#ServiceProperty(name = "osgi.command.scope", value = "scopeA")
String scope;
#ServiceProperty(name = "osgi.command.function", value = "{}")
String[] function = new String[] {
"test"
};
#Descriptor("Example")
public void test() {
System.out.println("hello");
}
}
The contents of B is simply:
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Provides;
#Component(immediate = true)
#Instantiate
#Provides
final class B {
}
Any ideas why the command is no longer listed? Tips to find more information on the state so that I can better debug this?
The problem is that commands needs the #Requires to be on a field rather than in the constructor.
#Requires
B b;
The constructor also must be removed.
This is because gogo has a special method of invoking the component.
also for me this needs to be changed
#ServiceProperty(name = "osgi.command.function", value = "{}")
String[] function = new String[] {
"test"
};
to
#ServiceProperty(name = "osgi.command.function", value = "{test}")
String[] function;