I am using jasperreports-6.14.0. As far as I can tell, there is only one way to add a custom hyperlink target to anything that allows hyperlinks. Please tell me there is a better way (other than putting javascript into my reference expression).
Implement the net.sf.jasperreports.engine.export.JRHyperlinkTargetProducer interface, looking in the hyperlink parameters for a specific, named parameter to return as your target string.
Extend net.sf.jasperreports.engine.export.HtmlExporter and set its targetProducerFactory protected field as an instance of your new custom hyperlink target producer.
It looks like this is the only option, but it just feels like there should be a way to skip step 2 by just setting the targetProducerFactory. It's almost like the Jasper devs started to do exactly that and thought "Nah, I just don't feel right about that. Let's take it out."
I am going to do the above unless some kind soul can show me a better way.
Custom target producers are loaded as extensions by the HTML exporter. You can register extensions either programmatically by creating the HTML exporter using your own JasperReportsContext instance, or package the extension in a jar and have it autodetected by the exporter.
If you control the HTML exporter creation you can pass the extension programmatically:
JRHyperlinkTargetProducer targetProducer = new JRHyperlinkTargetProducer() {
#Override
public String getHyperlinkTarget(JRPrintHyperlink hyperlink) {
return "foo";
}
};
JRHyperlinkTargetProducerMapFactory targetProducerFactory = new JRHyperlinkTargetProducerMapFactory();
targetProducerFactory.addProducer("mycustomtarget", targetProducer);
SimpleJasperReportsContext jasperReportsContext = new SimpleJasperReportsContext();
jasperReportsContext.setExtensions(JRHyperlinkTargetProducerFactory.class,
Collections.singletonList(targetProducerFactory));
HtmlExporter htmlExporter = new HtmlExporter(jasperReportsContext);
If you want to have the extension autodected you need to create a jar that contains a class like this:
public class CustomTargetProducerExtension implements ExtensionsRegistryFactory {
#Override
public ExtensionsRegistry createRegistry(String registryId, JRPropertiesMap properties) {
JRHyperlinkTargetProducer targetProducer = new JRHyperlinkTargetProducer() {
#Override
public String getHyperlinkTarget(JRPrintHyperlink hyperlink) {
return "bar";
}
};
JRHyperlinkTargetProducerMapFactory targetProducerFactory = new JRHyperlinkTargetProducerMapFactory();
targetProducerFactory.addProducer("mycustomtarget", targetProducer);
return new SingletonExtensionRegistry<>(JRHyperlinkTargetProducerFactory.class, targetProducerFactory);
}
}
And put a jasperreports_extension.properties resource in the root of the jar containing the line:
net.sf.jasperreports.extension.registry.factory.my.custom.target.producer=<package>.CustomTargetProducerExtension
Then your custom target producer would be automatically detected for elements that have hyperlinkTarget="mycustomtarget"
Related
I want to assign String content to a variable and enable the user to download that String as a text file.
I know there is FileDownloader from Vaadin 8 which I can use but I don't want static file/image download capability but rather dynamic String content which can downloaded a text file in the browser.
The resource interface in Vaadin, that is used by FileDownloader is a bit cumbersome. Especially so for dynamically created content, whose generation you'll want to postpone until user actually clicks the download button, which is very common in typical Vaadin apps.
Viritin add-on
Thus, I suggest to add Viritin add-on to your application. Use the DownloadButton component from it, which simplifies the usage a lot.
Here is a trivial example:
DownloadButton simple = new DownloadButton(out -> {
try {
out.write("Foobar".getBytes());
} catch (IOException ex) {
// exception handling
}
}).withCaption("Simple Download");
To see more complete example, refer to the tests in the Viritin project. From the project you can also see the "raw solution" if you peruse the source code of DownloadButton class.
PS. I'm the author of Viritin, so kind of biased to suggest its usage, but I have also been working with Vaadin for Vaadin Ltd for over a decade.
Here's a simple solution in Scala. We create a ReportDownloader component which extends VerticalLayout. It contains a "Download" button which trigger text file download in the browser.
import com.vaadin.ui.{Button, VerticalLayout}
import com.vaadin.server._
import java.io.{ByteArrayInputStream}
class VaadinStringStream(str : String, browserFileNameSuggestion : String) extends ConnectorResource {
override def getMIMEType: String = "text/plain"
override def getFilename: String = browserFileNameSuggestion
override def getStream: DownloadStream = new DownloadStream(
new ByteArrayInputStream(str.getBytes), getMIMEType, getFilename
)
}
class ReportDownloader extends VerticalLayout{
val btn = new Button("DOWNLOAD")
this addComponent btn
val stream = new VaadinStringStream("This is a sample txt file content", "report.txt")
new FileDownloader(stream).extend(btn)
}
VaadinStringStream takes dynamic String content and a suggested filename for the browser as arguments.
I try to extend my MyDSLProposalProvider from an external Eclipse RCP Project. I created an extension point schema which requires a class property which extends my ProposalProvider. In the new project I extend the class an overrode some methods justs to give me some output so I can see that the external method is called. But this is currently not happening. Is there anything I have to consider?
Currently the hirachy looks like:
MyDSLProposalProvider extends AbstractMyDSLProposalProvider
ExternalProposalProvider extends MyDSLProposalProvider
I rewrote a Method generated in the AbstractMyDSLProposalProvider but when its triggered the predefined Method in the AbstractMyDSLProposalProvider is called and not my new implementation.
public class ExternalMyDSLProposalPovider extends MyDSLProposalProvider
{
#Override
public void completeComponent_Name(EObject model, Assignment
assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
System.err.println("extern");
if(model instanceof Component)
{
createProposal("foo", "foo", context, acceptor);
}
super.completeComponent_Name(model, assignment, context, acceptor);
}
}
This is the class in the external Eclipse Project.
Thanks for the help.
When you declare an extension point using a schema that you have defined Eclipse puts that declaration in the extension point registry. That is all that is does, you must then write code to make uses of those declarations.
You read the extension point registry using something like:
IExtensionRegistry extRegistry = Platform.getExtensionRegistry();
IExtensionPoint extPoint = extRegistry.getExtensionPoint("your extension point id");
IConfigurationElement [] elements = extPoint.getConfigurationElements();
elements is now an array of the declarations in the various plugins using the extension point.
IConfigurationElement has various methods to get the values of the attributes of the declaration.
If you have defined a class in one of the attributes you can create an instance of the class using:
IConfigurationElement element = .... a config element
Object obj = element.createExecutableExtension("attribute name");
In your case the result should be your ExternalMyDSLProposalPovider.
You will then need to hook this object up with whatever is doing to proposals.
With the help of other Stackoverflow users, I have gone some way to my solution but have come to a halt.
I would like to build some generic classes in an app_code .cshtml file eg one would be to return property values from documents from a function eg
public static string docFieldValue(int docID,string strPropertyName){
var umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
var strValue = "";
try{
strValue = umbracoHelper.Content(docID).GetPropertyValue(strPropertyName).ToString();
}
catch(Exception ex){
strValue = "Error - invalid document field name (" + strPropertyName + ")";
}
var nContent = new HtmlString(strValue);
return nContent;
}
This works ok for returning one field (ie property) from a document. However, if I wanted to return 2 or more, ideally, I would store the returned node in a variable or class and then be able to fetch property values repeatedly without having to look up the document with each call
ie without calling
umbracoHelper.Content(docID).GetPropertyValue(strPropertyName).ToString();
with different strPropertyName parameters each time, as I assume that will mean multiple reads from the database).
I tried to build a class, with its properties to hold the returned node
using Umbraco.Web;
using Umbraco.Core.Models;
...
public static Umbraco.Web.UmbracoHelper umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
public static IPublishedContent docNode;
...
docNode = umbracoHelper.Content(docID);
but this crashed the code. Can I store the node in a property on a class, and if so, what type is it?
First of all, using a .cshtml file is unnecessary, use a .cs file instead :-) CSHTML files are for Razor code and HTML and stuff, CS files are for "pure" C#. That might also explain why your last idea crashes.
Second of all, UmbracoHelper uses Umbracos own cache, which means that the database is NOT touched with every request. I would at least define the umbracoHelper object outside of the method (so it gets reused every time the method is called instead of reinitialised).
Also, beware that property values can contain all kinds of other object types than strings.
EDIT
This is an example of the entire class file - my example namespace is Umbraco7 and my example class name is Helpers:
using Umbraco.Web;
namespace Umbraco7
{
public class Helpers
{
private static UmbracoHelper umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
private static dynamic docNode;
public static string docFieldValue(int docID, string strPropertyName)
{
docNode = umbracoHelper.Content(docID);
return docNode.GetPropertyValue(strPropertyName).ToString();
}
}
}
This is an example how the function is called inside a View (.cshtml file inside Views folder):
#Helpers.docFieldValue(1076, "introduction")
Helpers, again, is the class name I chose. It can be "anything" you want. I've just tested this and it works.
I suggest you read up on general ASP.NET MVC and Razor development, since this is not very Umbraco specific.
This question is probably going to illustrate a lack of knowledge on my part about how Groovy classes work, but I have tried to figure this out on my own with no luck. I want to create a getProperty() method on a class so I can reference member variables in a Groovyish way. This is NOT the same as just making them public because I do want some logic done when they are referenced. Basically, I'm trying to create a configuration Groovy class that uses ConfigSlurper:
class Configuration implements GroovyObject {
private static ConfigObject config = new ConfigSlurper().parse(new File("testing.conf").toURI().toURL())
//This method is illegal, but it illustrates what I want to do
public static String getProperty(String prop){
config.getProperty(prop)
}
}
If the above class were legal, I could then reference config items like so:
Configuration.dbUser
instead of this, which would require making the ConfigObject available:
Configuration.config.dbUser
I know, it would be worlds easier to just make the config object public, but knowing how to do this (or know why it's impossible) would help me understand Groovy a little better.
The only way I can get it to work is via the metaClass:
class Configuration {
private static ConfigObject config = new ConfigSlurper().parse( "foo = 'bar'" )
}
Configuration.metaClass.static.propertyMissing = { name ->
delegate.config[ name ]
}
println Configuration.foo
There may be a better way however...
I have a code template with a variable and I would like to capitalize(just the first letter) the value of this variable only in some occurrences. Is there a way to do this?
The template code is as follows - I would like to capitalize Property Name in my function names...
private $$${PropertyName};
${cursor}
public function get${PropertyName}()
{
return $$this->${PropertyName};
}
public function set${PropertyName}($$value)
{
$$this->${PropertyName} = $$value;
}
Please Note: This is a template for use with code templates in the IDE (not in PHP). For details see: http://www.ibm.com/developerworks/opensource/tutorials/os-eclipse-code-templates/index.html
I also want this and tried to build a custom TemplateVariableResolver to do it. (I already have one custom resolver in place that generates new UUIDs a la http://dev.eclipse.org/blogs/jdtui/2007/12/04/text-templates-2/.)
I made a custom resolver bound to capitalize:
public class CapitalizingVariableResolver extends TemplateVariableResolver {
#Override
public void resolve(TemplateVariable variable, TemplateContext context) {
#SuppressWarnings("unchecked")
final List<String> params = variable.getVariableType().getParams();
if (params.isEmpty())
return;
final String currentValue = context.getVariable(params.get(0));
if (currentValue == null || currentValue.length() == 0)
return;
variable.setValue(currentValue.substring(0, 1).toUpperCase() + currentValue.substring(1));
}
}
(plugin.xml:)
<extension point="org.eclipse.ui.editors.templates">
<resolver
class="com.foo.CapitalizingVariableResolver"
contextTypeId="java"
description="Resolves to the value of the variable named by the first argument, but with its first letter capitalized."
name="capitalized"
type="capitalize">
</resolver>
</extension>
that I would use like this: (I am working in Java; I see that you do not appear to be)
public PropertyAccessor<${propertyType}> ${property:field}() {
return ${property};
}
public ${propertyType} get${capitalizedProperty:capitalize(property)}() {
return ${property}.get();
}
public void set${capitalizedProperty}(${propertyType} ${property}) {
this.${property}.set(${property});
}
As of Eclipse 3.5, the problem I am having is that my custom resolver does not get a chance to re-resolve once I've specified a value for the property variable. It appears that the Java Development Tools (Eclipse JDT) do this dependent template variable re-resolution via a mechanism called MultiVariableGuess within the JavaContext (see addDependency()). Unfortunately for us, that mechanism does not seem to be exposed, so I/we can't use it to do the same (without lots of copy-and-paste or other redundant work).
At this point, I am giving up again for a while and will keep typing the leading-lowercase and leading-uppercase names separately into two independent template variables.