how to add datatable through type registry in cucumber 4.0 - cucumber

For cucumber 3.0 I was using
typeRegistry.defineDataTableType(DataTableType.entry(CustomData.class));
public class CustomData {
private int id;
private int val;
private Region region;
private Boolean isExisting;
private String type;
//getter and setter methods
}
How to convert this in cucumber 4.0.0 as part of configureTypeRegistry
My step in feature file as
When I set the custom data
| region | id | val | isExisting | type |
| NA | 2 | 10 | true | custom|

There are a few ways to do it. For #2 and #3 you'll have to add a dependency on jackson-databind to your project.
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.core.api.TypeRegistry;
import io.cucumber.core.api.TypeRegistryConfigurer;
import io.cucumber.datatable.DataTableType;
import java.util.Map;
class TypeRegistryConfiguration implements TypeRegistryConfigurer {
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public void configureTypeRegistry(TypeRegistry typeRegistry) {
// 1. Define the mapping yourself.
typeRegistry.defineDataTableType(
new DataTableType(MyType.class,
(Map<String, String> entry) -> {
MyType object = new MyType();
object.setX(entry.get("X"));
return object;
}
)
);
// 2. Define a data table type that delegates to an object mapper
typeRegistry.defineDataTableType(
new DataTableType(MyType.class,
(Map<String, String> entry) -> objectMapper.convertValue(entry, MyType.class)
)
);
// 3. Define a default data table entry that takes care of all mappings
typeRegistry.setDefaultDataTableEntryTransformer(
(entryValue, toValueType, cellTransformer) ->
objectMapper.convertValue(entryValue, objectMapper.constructType(toValueType)));
}
}
And in v5 you would do this like:
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DataTableType;
import io.cucumber.java.DefaultDataTableEntryTransformer;
import java.lang.reflect.Type;
import java.util.Map;
class TypeRegistryConfiguration {
private final ObjectMapper objectMapper = new ObjectMapper();
// 1. Define the mapping yourself
#DataTableType
public MyType myType(Map<String, String> entry) {
MyType object = new MyType();
object.setX(entry.get("X"));
return object;
}
// 2. Define a data table type that delegates to an object mapper
#DataTableType
public MyType myType(Map<String, String> entry) {
return objectMapper.convertValue(entry, MyType.class);
}
// 3. Define a default data table entry that takes care of all mappings
#DefaultDataTableEntryTransformer
public Object defaultDataTableEntry(Map<String, String> entry, Type toValueType) {
return objectMapper.convertValue(entry, objectMapper.constructType(toValueType));
}
}

Related

Could not find matching constructor for anonymous class

Consider the following code sample
class A {
int data
}
class B extends A {}
def o1 = new B(data: 1)
// This works correctly.
def o2 = new A(data:1) {}
// This will throw the following error
// Exception thrown
//
// groovy.lang.GroovyRuntimeException: Could not find matching constructor for: A(LinkedHashMap)
// at ConsoleScript2$1.<init>(ConsoleScript2)
// at ConsoleScript2.run(ConsoleScript2:11)
// at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
// at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
// at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
To me, the anonymous one should be the same as the named class. But it turns out that Groovy treats them differently. I want to know how to fix it. Thank you.
You see this error because of the nature of the dynamic map constructor - it is not added explicitly to the generated classes, but it is called through the CallSite.callConstructor(obj,map) method instead. However, there is a solution to that problem.
Consider the following exemplary test.groovy script:
class A {
int data
}
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
When you decompile generated A.class file, you will something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.Generated;
import groovy.transform.Internal;
import java.beans.Transient;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class A implements GroovyObject {
private int data;
#Generated
public A() {
CallSite[] var1 = $getCallSiteArray();
super();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
#Generated
#Internal
#Transient
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
#Generated
#Internal
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
#Generated
public int getData() {
return this.data;
}
#Generated
public void setData(int var1) {
this.data = var1;
}
}
This class has only one no-args constructor. When you decompile the test.class file (compiled Groovy script file), you will see something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
super();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));
var1[2].callCurrent(this, a1);
return var1[3].callCurrent(this, a2);
}
public class 1 extends A {
}
}
Take a look at how objects a1 and a2 are initialized. The a1 object is initialized in the following way:
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
It uses the CallSite.callConstructor() method to mimic the map constructor which does not exist in the A class. If we look at how the object a2 is initialized we will find this:
Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));
We can see that Groovy in the case of the anonymous class (which is not anonymous at all - Groovy generates a class anyway), Groovy uses a direct constructor call. And it fails, because there is no A(LinkedHashMap) constructor in the parent class.
Solution
Luckily, there is a solution to this problem - you can use #MapConstructor and #InheritConstructors annotations to force creating map constructor in the A class, and to inherit this constructor in the B class. Take a look at this working example:
import groovy.transform.InheritConstructors
import groovy.transform.MapConstructor
#MapConstructor
class A {
int data
}
#InheritConstructors
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
The only requirement is to use at least the Groovy 2.5 version which introduced the #MapConstructor annotation.

Using RowEditing and CheckBoxSelectionModel in a Grid Fails

In a GXT Grid I am attempting to use RowEditing and the CheckBoxSelectionModel. The Sencha Explorer Demo has examples of these in the Row Editable Grid and CheckBox Grid samples, but they don't show an example that includes a combination of these features. When I use both features on the same grid I am not getting the behavior that I had expected. If I click on the "selection" checkbox the row is placed into edit mode, where I would have expected the checkbox to just change from checked to unchecked or vice versa. In addition, when the row is placed into edit mode there is corruption on the line. Here is an example of a row from the grid prior to clicking on any of the values in that row:
and here is that row after clicking on one of the values:
Does anyone have any experience with this?
Update
Here's a sample class which demonstrates the issue:
package org.greatlogic.gxtgrid.client;
import java.util.ArrayList;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.sencha.gxt.core.client.IdentityValueProvider;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.grid.CheckBoxSelectionModel;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.grid.editing.GridRowEditing;
public class GXTGrid implements EntryPoint {
//-------------------------------------------------------------------------
#Override
public void onModuleLoad() {
ListStore<Pet> listStore = new ListStore<>(new ModelKeyProvider<Pet>() {
#Override
public String getKey(Pet pet) {
return Integer.toString(pet.getPetId());
}
});
IdentityValueProvider<Pet> ivp = new IdentityValueProvider<>();
CheckBoxSelectionModel<Pet> sm = new CheckBoxSelectionModel<>(ivp);
ArrayList<ColumnConfig<Pet, ?>> ccList = new ArrayList<>();
ccList.add(sm.getColumn());
ColumnConfig<Pet, String> cc1;
cc1 = new ColumnConfig<>(Pet.getPetNameValueProvider(), 100, "Name");
ccList.add(cc1);
ColumnModel<Pet> columnModel = new ColumnModel<>(ccList);
Grid<Pet> grid = new Grid<>(listStore, columnModel);
grid.setSelectionModel(sm);
grid.setView(new GridView<Pet>());
GridRowEditing<Pet> gre = new GridRowEditing<>(grid);
gre.addEditor(cc1, new TextField());
listStore.add(new Pet(1, "Lassie"));
listStore.add(new Pet(2, "Scooby"));
listStore.add(new Pet(3, "Snoopy"));
ContentPanel contentPanel = new ContentPanel();
contentPanel.add(grid);
RootLayoutPanel.get().add(contentPanel);
}
//-------------------------------------------------------------------------
private static class Pet {
private int _petId;
private String _petName;
public static ValueProvider<Pet, String> getPetNameValueProvider() {
return new ValueProvider<Pet, String>() {
#Override
public String getPath() {
return "Pet.PetName";
}
#Override
public String getValue(Pet pet) {
return pet._petName;
}
#Override
public void setValue(Pet pet, final String value) {
pet._petName = value;
}
};
}
public Pet(int petId, final String petName) {
_petId = petId;
_petName = petName;
}
public int getPetId() {
return _petId;
}
}
//-------------------------------------------------------------------------
}
This behavior of GridRowEditing with CheckBoxSelectionModel is completely normal.
I have used you code to try some things. I think the best way to use GridRowEditing and CheckBoxSelectionModel is, as I guessed, to start editing on double click, as nothing is provided to do it with just one click yet. To do so just add
gre.setClicksToEdit(ClicksToEdit.TWO);
Otherwise, if you really do not want to use two clicks to start editing, you can also use InlineRowEditing, which will enable you to use CheckBoxSelectionModel as you want.
Eventually, you might be able to override the whole behavior of GridRowEditing to handle CheckBoxSelectionModel properly on one click only, but it would be more complicated and require more specific knowledge of GXT framework that I don't have.
I haven't found a solution to this problem using the CheckBoxSelectionModel, and so I decided to try another approach, namely, adding a column to the grid that contains a checkbox, and handling the state of the selections manually. To do this I found that I needed to respond to a few events, which wasn't too bad. Here's a new version of the sample code, which should provide a starting point for a real implementation:
import java.util.ArrayList;
import java.util.TreeSet;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.sencha.gxt.cell.core.client.form.CheckBoxCell;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.form.CheckBox;
import com.sencha.gxt.widget.core.client.form.Field;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.grid.CellSelectionModel;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.grid.editing.GridRowEditing;
public class GXTGrid implements EntryPoint {
//-------------------------------------------------------------------------
private ListStore<Pet> _listStore;
private TreeSet<Integer> _selectedPetIdSet;
//-------------------------------------------------------------------------
#Override
public void onModuleLoad() {
_selectedPetIdSet = new TreeSet<>();
_listStore = new ListStore<>(new ModelKeyProvider<Pet>() {
#Override
public String getKey(Pet pet) {
return Integer.toString(pet.getPetId());
}
});
final CellSelectionModel<Pet> sm = new CellSelectionModel<>();
ArrayList<ColumnConfig<Pet, ?>> ccList = new ArrayList<>();
ValueProvider<Pet, Boolean> selectValueProvider;
selectValueProvider = new ValueProvider<GXTGrid.Pet, Boolean>() {
#Override
public String getPath() {
return "SelectCheckBox";
}
#Override
public Boolean getValue(Pet pet) {
return _selectedPetIdSet.contains(pet.getPetId());
}
#Override
public void setValue(Pet pet, final Boolean selected) { //
}
};
ColumnConfig<Pet, Boolean> cc0 = new ColumnConfig<>(selectValueProvider, 23, "");
CheckBoxCell checkBoxCell = new CheckBoxCell() {
#Override
protected void onClick(XElement parent, final NativeEvent event) {
super.onClick(parent, event);
Pet pet = sm.getSelectedItem();
if (!_selectedPetIdSet.remove(pet.getPetId())) {
_selectedPetIdSet.add(pet.getPetId());
}
}
};
cc0.setCell(checkBoxCell);
cc0.setFixed(true);
cc0.setHideable(false);
cc0.setMenuDisabled(true);
cc0.setResizable(false);
cc0.setSortable(false);
ccList.add(cc0);
ColumnConfig<Pet, String> cc1;
cc1 = new ColumnConfig<>(Pet.getPetNameValueProvider(), 100, "Name");
ccList.add(cc1);
ColumnModel<Pet> columnModel = new ColumnModel<>(ccList);
Grid<Pet> grid = new Grid<>(_listStore, columnModel);
grid.setSelectionModel(sm);
grid.setView(new GridView<Pet>());
GridRowEditing<Pet> gre = new GridRowEditing<>(grid);
Field<Boolean> checkBox = new CheckBox();
checkBox.setEnabled(false);
gre.addEditor(cc0, checkBox);
gre.addEditor(cc1, new TextField());
_listStore.add(new Pet(1, "Lassie"));
_listStore.add(new Pet(2, "Scooby"));
_listStore.add(new Pet(3, "Snoopy"));
ContentPanel contentPanel = new ContentPanel();
contentPanel.add(grid);
RootLayoutPanel.get().add(contentPanel);
}
//-------------------------------------------------------------------------
private static class Pet {
private int _petId;
private String _petName;
public static ValueProvider<Pet, String> getPetNameValueProvider() {
return new ValueProvider<Pet, String>() {
#Override
public String getPath() {
return "Pet.PetName";
}
#Override
public String getValue(Pet pet) {
return pet._petName;
}
#Override
public void setValue(Pet pet, final String value) {
pet._petName = value;
}
};
}
public Pet(int petId, final String petName) {
_petId = petId;
_petName = petName;
}
public int getPetId() {
return _petId;
}
}
//-------------------------------------------------------------------------
}

Creating a ToolTip Managed bean

This is a followup to my previous post at ToolTip Performance in XPages I have got the code to do it written (not tested) so I can't seem to get my Managed Bean to get called properly. My config contians the following:
<managed-bean id="ToolTip">
<managed-bean-name>WFSToolTip</managed-bean-name>
<managed-bean-class>ca.workflo.wfsToolTip.ToolTipText</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
and I have stripped my code down to the bare minimum:
package ca.workflo.wfsToolTip;
public class ToolTipText {
public String getToolTipText(String key){
return key;
}
}
My class is in the build path. I have a simple XPage with one filed on it and a tool tip for that field. The code for the tooltip is:
<xe:tooltip id="tooltip1" for="inputText1">
<xe:this.label>
<![CDATA[#{javascript:WFSToolTip.getToolTipText("More Stuff");}]]>
</xe:this.label>
</xe:tooltip>
When I load the test XPage in the browser I get an error that:
Error while executing JavaScript computed expression
Script interpreter error, line=1, col=12: Error calling method 'getToolTipText(string)' on java class 'ca.workflo.wfsToolTip.ToolTipText'
JavaScript code
1: WFSToolTip.getToolTipText("More Stuff");
I can't figure out why the call to getToolTipText would fail.
Can anyone see where I'm going wrong. This is my first Managed Bean and at the moment it is managing me rather than the other way around.
Thanks.
You need to:
- implement Serializable which boils down to state it and provide a version
- implement Map ... a little more work
Then you use Expression Language instead of SSJS. It would look like #{WFSToolTip["More Stuff"]}
This is how such a class would look like. You need to:
adjust the view name to reflect the name you want
the view needs to be flat, column 1 = tooltip name, column 2 = tooltip text
somewhere (on an admin/config page) you need to call WFSToolTip.clear(); (in SSJS) after you update the values in the configuration.
The example doesn't lazyload since running though a view navigator once is really fast. No point to do all these lookups.
Here you go:
package com.notessensei.xpages;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import lotus.domino.Base;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class Parameters implements Serializable, Map<String, String> {
private final static String CONFIG_VIEW = "keywords";
private static final long serialVersionUID = 1L;
private final Map<String, String> internalMap = new HashMap<String, String>();
public Parameters() {
this.populateParameters(internalMap);
}
private void populateParameters(Map<String, String> theMap) {
Database d = ExtLibUtil.getCurrentDatabase();
try {
View v = d.getView(CONFIG_VIEW);
ViewEntryCollection vec = v.getAllEntries();
ViewEntry ve = vec.getFirstEntry();
ViewEntry nextVe = null;
while (ve != null) {
nextVe = vec.getNextEntry(ve);
// Load the parameters, column 0 is the key, column 0 the value
Vector colVal = ve.getColumnValues();
theMap.put(colVal.get(0).toString(), colVal.get(1).toString());
// Cleanup
this.shred(ve);
ve = nextVe;
}
// recycle, but not the current database!!!
this.shred(ve, nextVe, vec, v);
} catch (NotesException e) {
e.printStackTrace();
}
}
public void clear() {
this.internalMap.clear();
this.populateParameters(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return this.internalMap.entrySet();
}
public String get(Object key) {
return this.internalMap.get(key);
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public String put(String key, String value) {
return this.internalMap.put(key, value);
}
public void putAll(Map<? extends String, ? extends String> m) {
this.internalMap.putAll(m);
}
public String remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<String> values() {
return this.internalMap.values();
}
private void shred(Base... morituri) {
for (Base obsoleteObject : morituri) {
if (obsoleteObject != null) {
try {
obsoleteObject.recycle();
} catch (NotesException e) {
// We don't care we want go get
// rid of it anyway
} finally {
obsoleteObject = null;
}
}
}
}
}
The difference to a regular HashMap is only the constructor that populates it. Hope that clarifies it.
I've never seen that id property.. My beans in faces-config look like this:
<managed-bean>
<managed-bean-name>CurrentJob</managed-bean-name>
<managed-bean-class>com.domain.inventory.Job</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Technically managed beans should implement Serializable and have a blank constructor. So you should have something like this inside :
public ToolTipText() {
}
I THINK you can get away without the Serializable for somethings... I always implement though but I'm sure you need the no argument constructor.
Thanks to all that have responded and helped out here especially Stephan Wissel. I thought I would post my version of Stephan's code, pretty much the same. There are issues with making the class an ApplicationScope because you need to shut down the HTTP task to refresh and reload the Class. What I did was added a button to the custom control where I to the view of the tooltips where I do the CRUD stuff and in the button do WFSToolTip().clear() and it rebuilds the map. Pretty neat. My next task for this is try to do the CRUD using JAVA and update the map directly. At the moment though I need to move on to my next task.
My next task revolves around a very similar Class. I have a master database that contains all the basic design and code. Then I have one or more applications that use that code and store the documents in their own database that contains the forms and views for that specific application. In the master I have created one or more application documents. Each of these documents contains the AppName (the key value) then the Map value is an array (Vector) containing the ReplicaID of the Application Database and a few other pieces of information. My class the loads a Map entry for each Application and collects a bunch of other information about the application from several places and stores that in the Map Value. At this point then I can set Database db = thisClass.getDatabase("App Name"). so a single custom control can be used for any/all of the applications. Pretty cool. I think I could get to like this.
Anyway here is the code I'm using for the ToolTips - BTW It has taken an XPage with about 175 fields and 100+ tooltips from being painfully slow to being acceptable. The good thing about it is that the XPage is creating a process profile document and once created it is not frequently modified as an admin action - not an everyday user action.
Please feel free point out error, omitions or suggestions to the code:
package ca.workflo.wfsToolTip;
import lotus.domino.Base;
import lotus.domino.Session;
import lotus.domino.Database;
import lotus.domino.View;
import lotus.domino.NotesException;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class ToolTipText implements Serializable, Map<String, String> {
private static final long serialVersionUID = 1L;
private Session s;
private String repID;
private Database db;
private Database helpDB;
private View helpView;
private ViewEntry ve;
private ViewEntry tVE;
private ViewEntryCollection veCol;
private final Map<String, String> internalMap = new HashMap<String, String>();
public ToolTipText() {
this.populateMap(internalMap);
}
private void populateMap(Map<String, String> theMap) {
try {
s = ExtLibUtil.getCurrentSession();
db = s.getCurrentDatabase();
repID = db.getProfileDocument("frmConfigProfile", "").getItemValue(
"WFSHelpRepID").firstElement().toString();
helpDB = s.getDbDirectory(null).openDatabaseByReplicaID(repID);
helpView = helpDB.getView("vwWFSToolTipHelp");
veCol = helpView.getAllEntries();
ve = veCol.getFirstEntry();
ViewEntry tVE = null;
while (ve != null) {
tVE = veCol.getNextEntry(ve);
Vector colVal = ve.getColumnValues();
theMap.put(colVal.get(0).toString(), colVal.get(1).toString());
recycleObjects(ve);
ve = tVE;
}
} catch (NotesException e) {
System.out.println(e.toString());
}finally{
recycleObjects(ve, tVE, veCol, helpView, helpDB);
}
}
public void clear() {
this.internalMap.clear();
this.populateMap(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return this.internalMap.entrySet();
}
public String get(Object key) {
try {
if (this.internalMap.containsKey(key)) {
return this.internalMap.get(key);
} else {
return "There is no Tooltip Help for " + key;
}
} catch (Exception e) {
return "error in tooltip get Object ";
}
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public String put(String key, String value) {
return this.internalMap.put(key, value);
}
public void putAll(Map<? extends String, ? extends String> m) {
this.internalMap.putAll(m);
}
public String remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<String> values() {
return this.internalMap.values();
}
public static void recycleObjects(Object... args) {
for (Object o : args) {
if (o != null) {
if (o instanceof Base) {
try {
((Base) o).recycle();
} catch (Throwable t) {
// who cares?
}
}
}
}
}
}

Creating xml from JAXB when one value is retrieved using a factory method

I have a UUID class where I get the uuid from a static factory method like
UUIDGenerator.getInstance().getUuid();
I have another class which has these UUids as a list.
class Artifact
{
Uuid uuid;
setUuid(Uuid uuid)
{
this.uuid = uuid;
}
Uuid getUuid()
{
return this.uuid;
}
}
Class ArtifactData
{
private List<Artifact> artifacts;
//setter for list
// getter for list
}
I want the xml to be created as
<ArtifactData>
<AssociatedArtifactList>
<ArtifactUuid>#some value<ArtifactUuid>
</AssociatedArtifactList>
</ArtifactData>
How do I create this xml out of Jaxb annotations. It complains on saying there isn't a public constructor for Uuid because it is constructed out of a factory method.
EDIT: The Uuid and Uuid generator cannot be modified. They are in a JAR. This is what I have tried so far
public class Artifact
{
#XmlJavaTypeAdapter(ArtifactUuidAdapter.class)
private Uuid uuid;
public Uuid getUuid()
{
return uuid;
}
public void setUuid(Uuid uuid)
{
this.uuid = uuid;
}
}
#XmlRootElement
public class ArtifactData
{
private List<Artifact> associatedArtifactList;
public List<Artifact> getArtifacts()
{
return associatedArtifactList;
}
#XmlElementWrapper(name="associatedArtifactList")
#XmlElement(name = "artifactUuid")
public void setArtifacts(List<Artifact> artifacts)
{
this.associatedArtifactList = artifacts;
}
}
public class ArtifactUuidAdapter extends XmlAdapter<Uuid, String>
{
#Override
public Uuid marshal(String uuid) throws Exception
{
return Uuid.getInstance(uuid);
}
#Override
public String unmarshal(Uuid uuid) throws Exception
{
return uuid.getData();
}
}
I still get an error called no arg default constructor is missing.
The #XmlType annotation allows you to configure a factory class and method. The factory class described in your questions doesn't quite meet the API requirements but you could easily create an adapter for it.
UUIDGeneratorAdapter
The class below would adapt your factory class to something that a JAXB (JSR-222) implementation could leverage.
public class UUIDGeneratorAdapter {
public static Uuid getUuid() {
return UUIDGenerator.getInstance().getUuid();
}
}
Uuid
Below is an example of how you configure the factory class through the #XmlType annotation.
import javax.xml.bind.annotation.XmlType;
#XmlType(factoryClass=UUIDGeneratorWrapper.class, factoryMethod="getUuid")
public class Uuid {
// ...
}
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html

JAXB use String as it is

I use REST and i was wondering if i can tell jaxb to insert a string field "as-it-is" into the outgoing xml.
Certainly i count unpack it before returning, but i would like to save this step.
#XmlRootElement(name="unnestedResponse")
public class Response{
#Insert annotation here ;-)
private String alreadyXml;
private int otherDate; ...
}
Is there a possability to tell JAXB to just use the String as it is without escapting? I want that the client does not have to parse my response and then parse this field.
greetings,
m
You can use the #XmlAnyElement and specify a DomHandler to keep a portion of the XML document as a String.
Customer
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private String bio;
#XmlAnyElement(BioHandler.class)
public String getBio() {
return bio;
}
public void setBio(String bio) {
this.bio = bio;
}
}
BioHandler
import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;
public class BioHandler implements DomHandler<String, StreamResult> {
private static final String BIO_START_TAG = "<bio>";
private static final String BIO_END_TAG = "</bio>";
private StringWriter xmlWriter = new StringWriter();
public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
return new StreamResult(xmlWriter);
}
public String getElement(StreamResult rt) {
String xml = rt.getWriter().toString();
int beginIndex = xml.indexOf(BIO_START_TAG) + BIO_START_TAG.length();
int endIndex = xml.indexOf(BIO_END_TAG);
return xml.substring(beginIndex, endIndex);
}
public Source marshal(String n, ValidationEventHandler errorHandler) {
try {
String xml = BIO_START_TAG + n.trim() + BIO_END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
For More Information
http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
Following bdoughan's answer did not work for me as I encountered errors during marshalling when the text contained the '& character (e.g. in URLs or when using HTML entities such as e.g. " ").
I was able to resolve this by changing the custom DomHandler's marshal method to
public Source marshal(String et, ValidationEventHandler veh) {
Node node = new SimpleTextNode(et);
return new DOMSource(node);
}
where SimpleTextNode implements the Node interface as follows:
class SimpleTextNode implements Node {
String nodeValue = "";
#Override
public SimpleTextNode(String nodeValue) {
this.nodeValue = nodeValue;
}
#Override
public short getNodeType() {
return TEXT_NODE;
}
// the remaining methods of the Node interface are not needed during marshalling
// you can just use the code template of your IDE...
...
}
PS: I would have loved to leave this as a comment to bdoughan's answer, but unfortunately I have way too little reputation :-(

Resources