Design - Check a condition before executing every method - java

I have a POJO named Document.java with 100+ member variables. There is a transformation layer, where I get the required data, transform it and store it in the Document class.
In the tranformation layer, I would like to set a member variable only if satisfies a certain criteria (based on available context).
So it would look something like this:
if(shouldGetExecuted1(context.getXXX())){
document.setField1(tranformDataForField1(availableData1));
}
if(shouldGetExecuted2(context.getXXX())){
document.setField2(tranformDataForField2(availableData2));
}
I want to do this for all the 100+ fields. Is there a clean way to do this?
Additional information
I don't want to use Strategy here as it would create too many classes as the no of strategies grow.

Try to use AOP. AspectJ allows you to define pointcuts (for example, some filtered set of methods) and control their execution via advices (before method call, after, around):
#Aspect
class ClassName {
...
#PointCut("call(public void ClassName.*(..))") //includes all void methods of ClassName object
public void myPointCut(){}
#Around("myPointCut()")
public void myLogicMethod(ProceedingJoinPoint thisJoinPoint) {
if(shouldGetExecuted1(context.getXXX())){
thisJoinPoint.proceed()
}
}
}
Here thisJoinPoint.proceed() will execute the body of the intercepted method.
Read docs about how to define pointcuts. In this example the same logic will be applied to all void methods of this class. You can define more accurate pointcuts via special expressions to provide different logic for each.

No, there is no clean way to do it in Java. You can find methods using reflection but there is no way to find variables such as "availableDataN". So you necessarily need to make "availableDataN" a field in order to find it using reflection.
The final code would be something as ugly as the following:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class X {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Method shouldGetExecuted = X.class.getMethod("shouldGetExecuted" + i, String.class);
boolean b = (boolean) shouldGetExecuted.invoke(null, context.getXXX());
if (b) {
Method tranformDataForField = X.class.getMethod("tranformDataForField");
Field data = X.class.getField("availableData" + i);
Object result = tranformDataForField.invoke(null, data.get(null));
Method set = X.class.getMethod("setField" + i, TransformDataType.class);
set.invoke(null, result);
}
}
}
}
You need to adapt to your specific case. For instance, here I am assuming all fields and methods are static. If they are not, then you need to replace null with an instance reference.

If you are consistent in the naming of your methods, reflection could help a lot.
The following code assumes the following:
A Document class with fields like xxx or xxYy (getters/setters would be usually present but are not required for the code to work)
A Transformer class that has
the capability to determine based on context information, if a field should be processed. These methods are named shouldTransformXxx(context).
the capability to transform the content of the field (with input and output of the same type as the corresponding field in Document). These methods are named T transformXxx(T).
A DataProvider class that has methods to provide the untransformed data. these methods are named findXxx()
The code below is pretty optimistic - it will fail, if a shouldTransformXxx for any field misses, or if it returns true, the same applies for the findXxx and transformXxx methods. So you would have to create classes with 100 methods each, which seems non-ideal for me. But on the other hand, having a class with 100 members seems to lead to awkward situations anyway...
So here's the code:
public class Document {
private String name;
private int size;
#Override
public String toString() {
return "Document [name=" + name + ", size=" + size + "]";
}
}
public class Transformer {
public enum ContextType {
NAME, SIZE
}
public boolean shouldTransformName(Set<ContextType> context) {
return context.contains(ContextType.NAME);
}
public boolean shouldTransformSize(Set<ContextType> context) {
return context.contains(ContextType.SIZE);
}
public String transformName(String name) {
return "::" + name;
}
public int transformSize(int size) {
return size + 1;
}
}
public class DataProvider {
private final String name;
private final int size;
public DataProvider(String name, int size) {
this.name = name;
this.size = size;
}
public String findName() {
return name;
}
public int findSize() {
return size;
}
}
public class Main {
private static final String TRANSFORM_METHOD_PREFIX = "transform";
private static final String CHECK_METHOD_PREFIX = "shouldTransform";
private static final String DATAPROVIDER_METHOD_PREFIX = "find";
private final DataProvider dataProvider;
private final Transformer transformer;
public Main(DataProvider dataProvider, Transformer transformer) {
this.dataProvider = dataProvider;
this.transformer = transformer;
}
public Document transformFields(Set<ContextType> context)
throws ReflectiveOperationException {
Document document = new Document();
for (Field field : Document.class.getDeclaredFields()) {
String capitalizedfieldName = capitalize(field.getName());
Class<?> fieldType = field.getType();
if (shouldTransform(context, capitalizedfieldName)) {
Object data = findData(capitalizedfieldName);
Object transformed = transformData(capitalizedfieldName,
fieldType, data);
// in presence of a security manager, a reflective call of the
// setter could be performed
field.setAccessible(true);
field.set(document, transformed);
}
}
return document;
}
private Object transformData(String capitalizedfieldName,
Class<?> fieldType, Object data)
throws ReflectiveOperationException {
String methodName = TRANSFORM_METHOD_PREFIX + capitalizedfieldName;
Method method = Transformer.class.getMethod(methodName, fieldType);
return method.invoke(transformer, data);
}
private Object findData(String capitalizedfieldName)
throws ReflectiveOperationException {
String methodName = DATAPROVIDER_METHOD_PREFIX + capitalizedfieldName;
Method method = DataProvider.class.getMethod(methodName);
return method.invoke(dataProvider);
}
private boolean shouldTransform(Set<ContextType> context,
String capitalizedfieldName) throws ReflectiveOperationException {
String methodName = CHECK_METHOD_PREFIX + capitalizedfieldName;
Method method = Transformer.class.getMethod(methodName, Set.class);
return (Boolean) method.invoke(transformer, context);
}
private String capitalize(String fieldName) {
char upperCaseFirstChar = Character.toUpperCase(fieldName.charAt(0));
if (fieldName.length() > 1) {
return upperCaseFirstChar + fieldName.substring(1);
} else {
return Character.toString(upperCaseFirstChar);
}
}
public static void main(String[] args) throws ReflectiveOperationException {
DataProvider dataProvider = new DataProvider("sample", 1);
Set<ContextType> context = EnumSet.of(ContextType.NAME,
ContextType.SIZE);
Main main = new Main(dataProvider, new Transformer());
Document document = main.transformFields(context);
System.out.println(document);
}
}

Related

How to avoid multiple inheritance in java

I am in a situation, where I'm trying to implement a (relatively simple) abstract syntax tree. All of the nodes inherit from a type called SimpleNode containing some code to store line and column information and accepting a visitor.
Now, some of the nodes should also be nameable, while others should have a property "accessible" (eg. public or private). Some nodes should even support both interfaces.
I'd preferably implement this using virtual inheritance and write two classes NameableNode and AccessibleNode, but Java doesn't support MI.
Eg NameableNode might have field "name" and implement simple getters and setters for this field. Similarly, AccessibleNode might also have a field "accessibility" and getters/setters.
What is a good way to implement this and avoid introducing code duplication in a huge part of the code base?
Small code example:
public class SimpleNode {
private int line = 0;
private int column = 0;
/* Getters and setters for line/column. */
/* ... */
}
public class NameableNode extends SimpleNode {
private String name = "";
/* Getters and setters for name */
}
public class AccessibleNode extends SimpleNode {
private boolean isPublic = false;
/* Getters and setters for accessibility */
}
You're looking for composition. There are many flavors of this - I will propose one that, from my understanding of what you're trying to build, should suit your purpose.
First, let's create some interfaces for yours Nodes:
public interface Nameable {
/* Getters and setters for name */
}
public interface Accessible {
/* Getters and setters for accessibility */
}
Next, you probably don't want to repeat the same implementation for every Node, so let's create those implementations:
public class NameDelegate() {
private String name = "";
/* Getters and setters for name */
}
public class AccessDelegate() {
private boolean isPublic = false;
/* Getters and setters for accessibility */
}
Now, let's put everything together:
public class SomeNodeA extends SimpleNode implements Nameable {
private NameDelegate nameDelegate;
public SomeNodeA(NameDelegate nameDelegate) {
this.nameDelegate = nameDelegate;
}
#Override
public String getName() {
return nameDelegate.getName();
}
#Override
public String setName(String name) {
nameDelegate.setName(name);
}
}
You can also have both behaviours in a single class:
public class SomeNodeB extends SimpleNode implements Nameable, Accessible {
private NameDelegate nameDelegate;
private AccessDelegate accessDelegate;
public SomeNodeB(NameDelegate nameDelegate, AccessDelegate accessDelegate) {
this.nameDelegate = nameDelegate;
this.accessDelegate = accessDelegate;
}
#Override
public String getName() {
return nameDelegate.getName();
}
#Override
public String setName(String name) {
nameDelegate.setName(name);
}
#Override
public boolean getAccessibility() {
return accessDelegate.getAccessibility();
}
/* etc... */
}
The idea is, you can package the state and the functionality of the different "features" into individual delegates, and expose them as corresponding interfaces in your Nodes.
Also, when operating on the Nodes, if you need to know whether a given instance of a Node supports a specific feature, you can use instanceof - e.g.:
if (someNode instanceof Nameable) {
// do naming stuff
}
In this case I would use the composition approach over inheritance:
public class Node {
private int line = 0;
private int column = 0;
/* Getters and setters for line/column. */
/* ... */
private String name = null;
public String getName() {
return this.name;
}
public void setName(String name) {
this._name = name;
}
private Boolean _isPublic = null;
public String isPublic() {
return this.name;
}
public void setIsPublic(boolean isPublic) {
this._isPublic = isPublic;
}
public boolean hasAccessibility() {
return this._isPublic != null;
}
public boolean hasName() {
return this._name != null;
}
}
Another solution that I like a bit more is creating these attributes dynamically using a HashMap and an enum that indicates all the possible attributes of a node. This way is more generic, as it requires to write less code for supporting new attributes, But it is also less typesafe(ish), as the additional attributes need to be casted at runtime:
import java.util.HashMap;
enum NodeAttribute {
NAME,
ACCESSIBILTY
}
enum NodeAccessibility {
PUBLIC,
PRIVATE
}
public class Node {
private int line = 0;
private int column = 0;
// Notice that this Object usage might involve some boxing for attributes of premitive type
private HashMap<NodeAttribute, Object> additionalAttributes = new HashMap<NodeAttribute, Object>();
/* Getters and setters for line/column. */
/* ... */
public boolean hetAttribute(NodeAttribute attribute) {
return this.additionalAttributes.containsKey(attribute);
}
public <T> T getAttributeValue(NodeAttribute attribute, Class<T> attributeClass) {
Object attributeValue = this.additionalAttributes.get(attribute);
// You may want to wrap the ClassCastException that may be raisen here to a more specfic error
T castedAttributeValue = attributeClass.cast(attributeValue);
return castedAttributeValue;
}
public void setAttributeValue(NodeAttribute attribute, Object value) {
// Notice that this implemintation allows changing the type of an existing attribute,
// If this is invalid behavior in your case you can throw an exception instead
this.additionalAttributes.put(attribute, value);
}
}
// Example usage
public class Program {
public static void main(String[] args) {
Node nodeWithNameOnly = new Node();
nodeWithNameOnly.setAttributeValue(NodeAttribute.NAME, 'node1');
Node nodeWithBoth = new Node();
nodeWithBoth.setAttributeValue(NodeAttribute.NAME, 'node2');
nodeWithBoth.setAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.PRIVATE);
Program.doStuffWithNode(nodeWithNameOnly);
/* output:
Node name: node1
*/
Program.doStuffWithNode(nodeWithBoth);
/* output:
Node name: node2
Node is public: False
*/
}
public static void doStuffWithNode(Node node) {
if (nodeWithNameOnly.hetAttribute(NodeAttribute.NAME)) {
String nodeName = nodeWithNameOnly.getAttributeValue(NodeAttribute.NAME, String.class);
system.out.println("Node name: " + nodeName);
}
if (nodeWithNameOnly.hetAttribute(NodeAttribute.ACCESSIBILTY)) {
NodeAccessibility nodeAccessibilty =
nodeWithNameOnly.getAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.class);
boolean nodeIsPublic = nodeAccessibilty == NodeAccessibility.PUBLIC;
system.out.println("Node is public: " + String.valueOf(nodeIsPublic));
}
}
}
In any case, this is the main rule of thumb - Inheritance should be used for an "is a" relation, whereas composition should be used for an "has a" relation.
For instance:
Fish extends Animal because a Fish is an Animal.
Post holds comments because a Post has comments.
And in our case, a node has a name and an accessibility level so it should hold them.

Reflection nested method invocations

I'm trying to define a fieldName->Method map that I can reuse for multiple instances. The idea is that I'll use reflection only once instead of for each instance.
For example, given the Client class below, the Map will have 'name' pointing to a Method object that corresponds to the getName() - Map<"name",Class.getName()>. Then I can invoke the getName() method on any instance of the Client class.
How do I deal with nested properties. For example, given the Currency class below, how do I get a Method object that will call Client.accounts[1].currency.code?
public class Client{
#Field( value = "name")
public String getName(){...}
#Field( value = "accounts" )
public List<Account> getAccounts(){...}
}
public class Account{
#Field( value = "acctNum")
public Long getAcctNum(){...}
#Field( value = "currency" )
public Currency getCurrency(){...}
}
public class Currency{
#Field( value = "code" )
public String getCode(){...}
}
First I'm creating a map of fieldName to Method. Something that I plan to do only once, and then reuse for object instances.
Map<String, Method> fieldMap = new HashMap();
for( Method method : Client.class.getDeclaredMethods() ){
Annotation annotation = method.getAnnotation(com.acme.Field.class);
if( annotation == null) {
continue;
}
com.acme.Field fieldMapping = (com.acme.mapping.Field) annotation;
String fieldName = fieldMapping.value();
fieldMap.put(fieldName, method);
}
Here's how I plan to reuse it
Client client = new Client();
for( Map.Entry<String, Method> entry : fieldMap.entrySet() ){
System.out.println(entry.getKey()+" -> "+entry.getValue().invoke(client, null));
}
For this purposes apache bean utils is very useful. Because it is one of the most popular java libraries.
But for small projects I use self written utility https://bitbucket.org/cache-kz/reflect4j/overview
It supports POJO style of objects, with public fields.
Ex:
public class A {
public int a;
public int b;
}
Or Java Bean style of objects, with getters and setters.
Ex:
public class A {
protected int a;
protected int b;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB(){ ... }
public void setB(int b) { ... }
}
Mixed objects supported too.
For your example code will be simple:
import kz.cache.reflect4j.ObjectManager
ObjectManager manag = new ObjectManager();
Object currencyCode = reader.getValue("accounts[1].currency.code", instanceOfClient);
other examples can be found in test package:
https://bitbucket.org/cache-kz/reflect4j/src/c344d76a4896235f6a1b09b2499d5ae25adc2900/src/test/java/kz/cache/reflect4j/ObjectManagerTest.java

How to use apache storm tuple

I just began with Apache Storm. I read the tutorial and had a look into examples My problem is that all example work with very simple tuples (often one filed with a string). The tuples are created inline (using new Values(...)). In my case i have tuples with many fields (5..100). So my question is how to implement such tuple with name and type (all primitive) for each field?
Are there any examples? (i think directly implementing "Tuple" isn't a good idea)
thanks
An alternative to creating the tuple with all of the fields as a value is to just create a bean and pass that inside the tuple.
Given the following class:
public class DataBean implements Serializable {
private static final long serialVersionUID = 1L;
// add more properties as necessary
int id;
String word;
public DataBean(int id, String word) {
setId(id);
setWord(word);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
}
Create and emit the DataBean in one bolt:
collector.emit(new Values(bean));
Get the DataBean in the destination bolt:
#Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
try {
DataBean bean = (DataBean)tuple.getValue(0);
// do your bolt processing with the bean
} catch (Exception e) {
LOG.error("WordCountBolt error", e);
collector.reportError(e);
}
}
Don't forget to make your bean serializable and register when you set up your topology:
Config stormConfig = new Config();
stormConfig.registerSerialization(DataBean.class);
// more stuff
StormSubmitter.submitTopology("MyTopologyName", stormConfig, builder.createTopology());
Disclaimer: Beans will work fine for shuffle grouping. If you need to do a fieldsGrouping, you should still use a primitive. For example, in the Word Count scenario, you need go group by word so you might emit:
collector.emit(new Values(word, bean));
I would implement a custom tuple/value type as follows: Instead of using member variables to store the data, each attribute is mapped to a fixed index into the object list of the inherited Values types. This approach avoids the "field grouping" problem a regular Bean.
it in not required to add additional attributes for fields grouping (what is quite unnatural)
data duplication is avoided (reducing the number of shipped bytes)
it preserves the advantage of the beans pattern
An example for word count example would be something like this:
public class WordCountTuple extends Values {
private final static long serialVersionUID = -4386109322233754497L;
// attribute indexes
/** The index of the word attribute. */
public final static int WRD_IDX = 0;
/** The index of the count attribute. */
public final static int CNT_IDX = 1;
// attribute names
/** The name of the word attribute. */
public final static String WRD_ATT = "word";
/** The name of the count attribute. */
public final static String CNT_ATT = "count";
// required for serialization
public WordCountTuple() {}
public WordCountTuple(String word, int count) {
super.add(WRD_IDX, word);
super.add(CNT_IDX, count);
}
public String getWord() {
return (String)super.get(WRD_IDX);
}
public void setWort(String word) {
super.set(WRD_IDX, word);
}
public int getCount() {
return (Integer)super.get(CNT_IDX);
}
public void setCount(int count) {
super.set(CNT_IDX, count);
}
public static Fields getSchema() {
return new Fields(WRD_ATT, CNT_ATT);
}
}
To avoid inconsistencies, final static variables for "word" and "count" attribute are used. Furthermore, a method getSchema() returns the implemented schema to be used to declare output streams in Spout/Bolt method .declareOutputFields(...)
For output tuples, this type can be used straight forward:
public MyOutBolt implements IRichBolt {
#Override
public void execute(Tuple tuple) {
// some more processing
String word = ...
int cnt = ...
collector.emit(new WordCountTuple(word, cnt));
}
#Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(WordCountTuple.getSchema());
}
// other methods omitted
}
For input tuples, I would suggest the following pattern:
public MyInBolt implements IRichBolt {
// use a single instance for avoid GC trashing
private final WordCountTuple input = new WordCountTuple();
#Override
public void execute(Tuple tuple) {
this.input.clear();
this.input.addAll(tuple.getValues());
String word = input.getWord();
int count = input.getCount();
// do further processing
}
// other methods omitted
}
MyOutBolt and MyInBolt can be connected as follows:
TopologyBuilder b = ...
b.setBolt("out", new MyOutBolt());
b.setBolt("in", new MyInBolt()).fieldsGrouping("out", WordCountTuple.WRD_ATT);
Using fields grouping is straight forward, because WordCountTuple allows to access each attribute individually.

Editing a complex Java object in a Tapestry 5 web application

I am using Tapestry 5.3.6 for a web application and I want the user to edit an instance of a Java class (a "bean", or POJO) using a web form (which immediately suggests the use of beaneditform) - however the Java class to be edited has a fairly complex structure. I am looking for the simplest way of doing this in Tapestry 5.
Firstly, lets define some utility classes e.g.
public class ModelObject {
private URI uri;
private boolean modified;
// the usual constructors, getters and setters ...
}
public class Literal<T> extends ModelObject {
private Class<?> valueClass;
private T value;
public Literal(Class<?> valueClass) {
this.valueClass = valueClass;
}
public Literal(Class<?> valueClass, T value) {
this.valueClass = valueClass;
this.value = value;
}
// the usual getters and setters ...
}
public class Link<T extends ModelObject> extends ModelObject {
private Class<?> targetClass;
private T target;
public Link(Class<?> targetClass) {
this.targetClass = targetClass;
}
public Link(Class<?> targetClass, T target) {
this.targetClass = targetClass;
this.target = target;
}
// the usual getters and setters ...
}
Now you can create some fairly complex data structures, for example:
public class HumanBeing extends ModelObject {
private Literal<String> name;
// ... other stuff
public HumanBeing() {
name = new Literal<String>(String.class);
}
// the usual getters and setters ...
}
public class Project extends ModelObject {
private Literal<String> projectName;
private Literal<Date> startDate;
private Literal<Date> endDate;
private Literal<Integer> someCounter;
private Link<HumanBeing> projectLeader;
private Link<HumanBeing> projectManager;
// ... other stuff, including lists of things, that may be Literals or
// Links ... e.g. (ModelObjectList is an enhanced ArrayList that remembers
// the type(s) of the objects it contains - to get around type erasure ...
private ModelObjectList<Link<HumanBeing>> projectMembers;
private ModelObjectList<Link<Project>> relatedProjects;
private ModelObjectList<Literal<String>> projectAliases;
// the usual constructors, getters and setters for all of the above ...
public Project() {
projectName = new Literal<String>(String.class);
startDate = new Literal<Date>(Date.class);
endDate = new Literal<Date>(Date.class);
someCounter = new Literal<Integer>(Integer.class);
projectLeader = new Link<HumanBeing>(HumanBeing.class);
projectManager = new Link<HumanBeing>(HumanBeing.class);
projectMembers = new ModelObjectList<Link<HumanBeing>>(Link.class, HumanBeing.class);
// ... more ...
}
}
If you point beaneditform at an instance of Project.class, you will not get very far before you have to supply a lot of custom coercers, translators, valueencoders, etc - and then you still run into the problem that you can't use generics when "contributing" said coercers, translators, valueencoders, etc.
I then started writing my own components to get around these problems (e.g. ModelObjectDisplay and ModelObjectEdit) but this would require me to understand a lot more of the guts of Tapestry than I have time to learn ... it feels like I might be able to do what I want using the standard components and liberal use of "delegate" etc. Can anyone see a simple path for me to take with this?
Thanks for reading this far.
PS: if you are wondering why I have done things like this, it is because the model represents linked data from an RDF graph database (aka triple-store) - I need to remember the URI of every bit of data and how it relates (links) to other bits of data (you are welcome to suggest better ways of doing this too :-)
EDIT:
#uklance suggested using display and edit blocks - here is what I had already tried:
Firstly, I had the following in AppPropertyDisplayBlocks.tml ...
<t:block id="literal">
<t:delegate to="literalType" t:value="literalValue" />
</t:block>
<t:block id="link">
<t:delegate to="linkType" t:value="linkValue" />
</t:block>
and in AppPropertyDisplayBlocks.java ...
public Block getLiteralType() {
Literal<?> literal = (Literal<?>) context.getPropertyValue();
Class<?> valueClass = literal.getValueClass();
if (!AppModule.modelTypes.containsKey(valueClass))
return null;
String blockId = AppModule.modelTypes.get(valueClass);
return resources.getBlock(blockId);
}
public Object getLiteralValue() {
Literal<?> literal = (Literal<?>) context.getPropertyValue();
return literal.getValue();
}
public Block getLinkType() {
Link<?> link = (Link<?>) context.getPropertyValue();
Class<?> targetClass = link.getTargetClass();
if (!AppModule.modelTypes.containsKey(targetClass))
return null;
String blockId = AppModule.modelTypes.get(targetClass);
return resources.getBlock(blockId);
}
public Object getLinkValue() {
Link<?> link = (Link<?>) context.getPropertyValue();
return link.getTarget();
}
AppModule.modelTypes is a map from java class to a String to be used by Tapestry e.g. Link.class -> "link" and Literal.class -> "literal" ... in AppModule I had the following code ...
public static void contributeDefaultDataTypeAnalyzer(
MappedConfiguration<Class<?>, String> configuration) {
for (Class<?> type : modelTypes.keySet()) {
String name = modelTypes.get(type);
configuration.add(type, name);
}
}
public static void contributeBeanBlockSource(
Configuration<BeanBlockContribution> configuration) {
// using HashSet removes duplicates ...
for (String name : new HashSet<String>(modelTypes.values())) {
configuration.add(new DisplayBlockContribution(name,
"blocks/AppPropertyDisplayBlocks", name));
configuration.add(new EditBlockContribution(name,
"blocks/AppPropertyEditBlocks", name));
}
}
I had similar code for the edit blocks ... however none of this seemed to work - I think because the original object was passed to the "delegate" rather than the de-referenced object which was either the value stored in the literal or the object the link pointed to (hmm... should be [Ll]inkTarget in the above, not [Ll]inkValue). I also kept running into errors where Tapestry couldn't find a suitable "translator", "valueencoder" or "coercer" ... I am under some time pressure so it is difficult to follow these twisty passages through in order to get out of the maze :-)
I would suggest to build a thin wrapper around the Objects you would like to edit though the BeanEditForm and pass those into it. So something like:
public class TapestryProject {
private Project project;
public TapestryProject(Project proj){
this.project = proj;
}
public String getName(){
this.project.getProjectName().getValue();
}
public void setName(String name){
this.project.getProjectName().setValue(name);
}
etc...
}
This way tapestry will deal with all the types it knows about leaving you free of having to create your own coersions (which is quite simple in itself by the way).
You can contribute blocks to display and edit your "link" and "literal" datatypes.
The beaneditform, beaneditor and beandisplay are backed by the BeanBlockSource service. BeanBlockSource is responsible for providing display and edit blocks for various datatypes.
If you download the tapestry source code and have a look at the following files:
tapestry-core\src\main\java\org\apache\tapestry5\corelib\pages\PropertyEditBlocks.java
tapestry-core\src\main\resources\org\apache\tapestry5\corelib\pages\PropertyEditBlocks.tml
tapestry-core\src\main\java\org\apache\tapestry5\services\TapestryModule.java
You will see how tapestry contributes EditBlockContribution and DisplayBlockContribution to provide default blocks (eg for a "date" datatype).
If you contribute to BeanBlockSource, you could provide display and edit blocks for your custom datatypes. This will require you reference blocks by id in a page. The page can be hidden from your users by annotating it with #WhitelistAccessOnly.
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/BeanBlockSource.html
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/DisplayBlockContribution.html
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/EditBlockContribution.html
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/WhitelistAccessOnly.html
Here's an example of using an interface and a proxy to hide the implementation details from your model. Note how the proxy takes care of updating the modified flag and is able to map URI's from the Literal array to properties in the HumanBeing interface.
package com.github.uklance.triplestore;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class TripleStoreOrmTest {
public static class Literal<T> {
public String uri;
public boolean modified;
public Class<T> type;
public T value;
public Literal(String uri, Class<T> type, T value) {
super();
this.uri = uri;
this.type = type;
this.value = value;
}
#Override
public String toString() {
return "Literal [uri=" + uri + ", type=" + type + ", value=" + value + ", modified=" + modified + "]";
}
}
public interface HumanBeing {
public String getName();
public void setName(String name);
public int getAge();
public void setAge();
}
public interface TripleStoreProxy {
public Map<String, Literal<?>> getLiteralMap();
}
#Test
public void testMockTripleStore() {
Literal<?>[] literals = {
new Literal<String>("http://humanBeing/1/Name", String.class, "Henry"),
new Literal<Integer>("http://humanBeing/1/Age", Integer.class, 21)
};
System.out.println("Before " + Arrays.asList(literals));
HumanBeing humanBeingProxy = createProxy(literals, HumanBeing.class);
System.out.println("Before Name: " + humanBeingProxy.getName());
System.out.println("Before Age: " + humanBeingProxy.getAge());
humanBeingProxy.setName("Adam");
System.out.println("After Name: " + humanBeingProxy.getName());
System.out.println("After Age: " + humanBeingProxy.getAge());
Map<String, Literal<?>> literalMap = ((TripleStoreProxy) humanBeingProxy).getLiteralMap();
System.out.println("After " + literalMap);
}
protected <T> T createProxy(Literal<?>[] literals, Class<T> type) {
Class<?>[] proxyInterfaces = { type, TripleStoreProxy.class };
final Map<String, Literal> literalMap = new HashMap<String, Literal>();
for (Literal<?> literal : literals) {
String name = literal.uri.substring(literal.uri.lastIndexOf("/") + 1);
literalMap.put(name, literal);
}
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(TripleStoreProxy.class)) {
return literalMap;
}
if (method.getName().startsWith("get")) {
String name = method.getName().substring(3);
return literalMap.get(name).value;
} else if (method.getName().startsWith("set")) {
String name = method.getName().substring(3);
Literal<Object> literal = literalMap.get(name);
literal.value = args[0];
literal.modified = true;
}
return null;
}
};
return type.cast(Proxy.newProxyInstance(getClass().getClassLoader(), proxyInterfaces, handler));
}
}

How to change the html of a HTMLPanel

I want do declare a Subclass of an HTMLPanel.
In its constructor I want to give it a few paramters to construct the containing html.
Because I have to call the super-constructor as first statement, I have to change the html later in the constructor.
How can I do this?
public class MyHTMLPanel extends HTMLPanel
{
public MyHTMLPanel(String id, int anotherParameter)
{ super("");
String html=""
// ... some code th construct the html
//??? this.setHtml(html);
}
}
You can find below an example I used and worked well for me.
I don't remember why I don't sub-class HTMLPanel, whether a good reason or not.
You will notice a mechanism to randomize the html ids in case you include several objects of the same type in a single page.
public abstract class HtmlPanelBase extends Composite
{
private String _dynPostfix = "";
protected final String id(final String staticId) { return staticId + _dynPostfix; }
private final String wrapId(final String id) { return "id=\"" + id + "\""; }
private final String wrapDynId(final String refId) { return wrapId(id(refId)); }
private String _htmlAsText = null;
public String getHtmlAsText() { return _htmlAsText; }
abstract protected String htmlPanelBundleHtmlText();
abstract protected List<String> idList();
protected HTMLPanel _holder = null;
private HTMLPanel createHtmlPanel(final boolean defineGloballyUniqueIds)
{
// Referent HTML panel text containing the reference id's.
_htmlAsText = htmlPanelBundleHtmlText();
if (defineGloballyUniqueIds)
{
// List of id's in the HTML Panel reference page to replace with dynamic/unique id's.
final List<String> refIdList = idList();
// Replace the reference id's with dynamic/unique id's.
for (String refId : refIdList)
_htmlAsText = _htmlAsText.replace(wrapId(refId), wrapDynId(refId));
}
// Return the HTMLPanel containing the globally unique id's.
return new HTMLPanel(_htmlAsText);
}
public HtmlPanelBase(final boolean defineGloballyUniqueIds)
{
setup(defineGloballyUniqueIds);
initWidget(_holder);
}
private void setup(final boolean defineGloballyUniqueIds)
{
if (defineGloballyUniqueIds)
_dynPostfix = "_" + UUID.uuid().replace("-", "_");
_holder = createHtmlPanel(defineGloballyUniqueIds);
}
}
And now how you could sub-class from the above base:
public class HtmlPanelTemplate extends HtmlPanelBase
{
private final static boolean _defineGloballyUniqueIds = false;
private final static int _numIdCapacity = 40;
public HtmlPanelTemplate()
{
super(_defineGloballyUniqueIds);
setup();
}
#Override
protected String htmlPanelBundleHtmlText()
{
return YourClientBundle.INSTANCE.getYourFileHtml().getText();
}
#Override
protected List<String> idList()
{
final List<String> idList = new ArrayList<String>(_numIdCapacity);
return idList;
}
private void setup()
{
}
}
You don't need to subclass HTMLPanel. You can create a simple Composite widget:
public class myPanel extends Composite {
private HTMLPanel panel = new HTMLPanel();
public myPanel(String id, int anotherParameter) {
// set HTML to panel based on your parameters
initWidget(panel);
}
}
htmlPanel.getElement().setInnerHTML(...)
Don't know whether this works in derived class' constructor. But setting up a class for specific content text isn't really a good solution.

Categories

Resources