Set value knowing the EObject and its EAttribute - java

I want to set the value of an object EObject knowing it's EAttribute. Is that possible?
I can use reflections, build the method name and invoke it, but is there a better way to achieve that? Maybe some EMF Util classes?
public static Object invokeMethodBy(EObject object, EAttribute attribute, Object...inputParameters){
String attrName = attribute.getName().substring(0, 1).toUpperCase() + attribute.getName().substring(1);
Object returnValue = null;
try {
returnValue = object.getClass().getMethod("set"+attrName, boolean.class).invoke(object,inputParameters);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException
| SecurityException e1) {
e1.printStackTrace();
}
return returnValue;
}

EMF has already its own introspection mechanisms, that do not use Java Reflection but use static generated code.
What you need is this:
object.eSet(attribute, value);
If the attribute is a "many" relationship, such as a List, you need to retrieve the list before and then add the content to the list:
if (attribute.isMany()) {
List<Object> list = (List<Object>) object.eGet(attribute);
list.addAll(value);
}
In case you don't have the EAttribute but have the name of the attribute (as String) you can also retrieve the EStructuralFeature by name using the EClass metadata:
EStructuralFeature feature = object.eClass.getEStructuralFeature(attributeName);
object.eSet(feature, value);
You should look to the EObject API, in special the methods starting by "e". EcoreUtil class has also useful methods.

Related

How to get the property data type in FileNet P8

In FileNet P8, I need to get the datatype of the property of a custom class using JAVA API. Is there any way to do the same?
This should give you an idea of what you need to do:
//SymbolicName of the property we will search for.
String strSearchName = PropertyNames.DATE_LAST_MODIFIED;
//Document (or other object) that we will use to get classDescription.
Document document = (Document) arg0;
PropertyDescription objPropDesc = null;
PropertyDescriptionList pdl = document.get_ClassDescription().get_PropertyDescriptions();
Iterator<?> iter = pdl.iterator();
while (iter.hasNext())
{
objPropDesc = (PropertyDescription) iter.next();
// Get SymbolicName property from the property cache
String strPropDescSymbolicName = objPropDesc.get_SymbolicName();
if (strPropDescSymbolicName.equalsIgnoreCase(strSearchName))
{
// PropertyDescription object found
System.out.println("Property description selected: " + strPropDescSymbolicName);
System.out.println(objPropDesc);
TypeID type = objPropDesc.get_DataType();
System.out.println(type.toString());
break;
}
}
The idea is to:
Take an object (Document in this case).
Get its Class Description.
Get the list of Property Descriptions from the Class Description.
Loop through the Property Descritpions until you locate the Description you are trying to find.
Get the TypeId from the Property Description.
The TypeId contains the information you need to know what the Type is of the Property.
I borrowed code from here : Working with Properties
You should also familiarize yourself with this : Properties
Edit to add a different method:
In the case of creating documents, we need to be able to obtain the Class Description object. This means we will need to perform additional round trips.
// Get the ClassDescription
String strSymbolicName = "myClassName";
ClassDescription objClassDesc = Factory.ClassDescription.fetchInstance(myObjStore, strSymbolicName, null);
// find the PropertyDescription
PropertyDescription pds = null;
PropertyDescriptionList pdl = objClassDesc.get_PropertyDescriptions()‌​;
Iterator<?> itr = pdl.iterator();
while(itr.hasNext()){
pds = (PropertyDescription) itr.next();
System.out.println("Symbolic Name is "+pds.get_SymbolicName()+" DataType is "+pds.get_DataType().toString());
}
// You can now use it in a loop of several documents if you wish.
...
Take a look here as well : Working With Classes
Christopher Powell's answer is correct, there is one thing it does not cover, though (depending on the definition of the custom class in question). Consider this as a "best practice" or just an extension of the code borrowed from the URL that Christopher mentioned.
The FileNet P8 class hierarchy allows for inheritance of property definitions. Simple example: one can search through the 'Document' class - which is the root class of the class hierarchy - of an object store, and use some property of a subclass in the search sql. Imagine Subclass1 as an immediate subclass of Document. Subclass1 has a property Property1. Even if Document does not have this property in its class description, a search in Document with Property1='somevalue' will return objects of Subclass1 (if there is a match with 'somevalue').
However,
objClassDesc.get_PropertyDescriptions()‌​;
won't return property descriptions of subclasses, thus you might end up with API_PROPERTY_NOT_IN_CACHE errors.
To give you a good starting point if you are facing this case, look at below code:
PropertyDescriptionList ownProps = objClassDesc.get_PropertyDescriptions();
PropertyDescriptionList subclassProps = null;
if (objClassDesc.get_HasProperSubclassProperties()) {
logger.debug("Document class '"+documentClassname+"' supports 'include descendant properties' queries, including descendant properties.");
subclassProps = objClassDesc.get_ProperSubclassPropertyDescriptions();
}
List<PropertyDescription> result = mergePropertyDescriptionLists(ownProps, subclassProps);
If you need to merge those two lists, you are better off using a List of PropertyDescription objects instead of PropertyDescriptionList: the ones returned by the server are read-only.
#SuppressWarnings("unchecked")
protected List<PropertyDescription> mergePropertyDescriptionLists(PropertyDescriptionList list1, PropertyDescriptionList list2) throws Exception {
try {
#SuppressWarnings("unchecked")
List<PropertyDescription> mergedList = new ArrayList<PropertyDescription>();
if (list1 != null && (list1.size() > 0)) {
mergedList.addAll(list1);
}
if (list2 != null && (list2.size() > 0)) {
mergedList.addAll(list2);
}
return mergedList;
} catch (Throwable t) {
throw new Exception("Failed to merge property description lists.", t);
}
}

PlayFramework 2.5 - Ebean update using reflection

I'm developing a website with an inline editor using Play framework 2.5 and Ebean as ORM, and I have a news section where the admin can edit every single news (editing fields inline such as title, content and so on).
In order to do so, I set every html element which can be modified with an id equals to the news model field (e.g. the html element mapping the field title will have id="title"), then when I receive data from the client, I use reflection on the controller to map every content with the correct news field.
Here is the code (EditContent is an object which contains informations like the id and the htmlContent of every modified content):
News news = News.find.byId(newsId);
for(EditContent content : pageContents.contents) {
Field field = news.getClass().getField(content.cssId);
field.setAccessible(true);
field.set(news, content.htmlContent);
}
news.update();
The problem is that the update seems to be executed, but actually values are not updated on db. Using debugger, I inspected the object news and I can see that the fields are modified properly, but then the update has no effects on db.
Also, I noticed that the same code using:
News news = new News()
...
//reflection to save modifed contents in the new object
...
news.save()
works as I expect, saving a new row in the database.
Any idea?
Thank you in advance for your help!
You are setting the field values rather than calling a setter method.
So for the update() ... Ebean is not aware of which properties have been changed - it thinks none have changed.
Play modifies field put calls into method calls via enhancement. So this is possibly why you think these reflection field set values might work.
So basically, as #Rob Bygrave said... The setter method should be invoked here rather than setting the field value directly cause the ebean will ignore the new value if you set the value to the corresponding field directly. It seems that the play framework following the Java bean convention, so basically we can guess what the set name called.
Here is an example code to update User's information dynamically:
private final String[] userUpdatableNames = { "name", "password", "allowGPS" };
...
JsonNode dateForm = request().body().asJson();
Field field;
Class<?> type;
Method method;
for (int i = 0; i < userUpdatableNames.length; i++) {
if (isArgs[i]) {
try {
field = target.getClass().getDeclaredField(userUpdatableNames[i]);
type = field.getType();
Method method = target.getClass().getMethod("set" + initialUpperize(userUpdatableNames[i]), type);
method.invoke(target, convert(type,dateForm.findValue(userUpdatableNames[i]).textValue()));
}catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException
| NoSuchMethodException | InvocationTargetException e) {
return internalServerError(Json.toJson("Invoke exception"));
}
}
}
...
public String initialUpperize(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
...
private Object convert(Class<?> targetType, String text) {
PropertyEditor editor = PropertyEditorManager.findEditor(targetType);
editor.setAsText(text);
return editor.getValue();
}
where isArgs is a boolean array to mark whether the field is in Json body...
Thanks

Reducing the cyclomatic complexity, multiple if statements

I have the following code:
private Facility updateFacility(Facility newFacility, Facility oldFacility) {
if (newFacility.getCity() != null)
oldFacility.setCity(newFacility.getCity());
if (newFacility.getContactEmail() != null)
oldFacility.setContactEmail(newFacility.getContactEmail());
if (newFacility.getContactFax() != null)
oldFacility.setContactFax(newFacility.getContactFax());
if (newFacility.getContactName() != null)
oldFacility.setContactName(newFacility.getContactName());
// ......
}
There are around 14 such checks and assignments. That is, except for a few, I need to modify all the fields of the oldFacility object. I'm getting a cyclomatic complexity of this code 14, which is "greater than 10 authorized" as per SonarQube. Any ideas upon how to reduce the cyclomatic complexity?
At some point in your program, you will have to implement the logic:
If the new facility has a property defined, update the old facility accordingly
If not, do not override the previous value from the old facility.
Without having a global look at your project, what you can do is to move that logic inside the setters of each property:
public class Facility {
public void setSomething(String something) {
if (something != null) {
this.something = something;
}
}
}
This way, your update method would simply be:
private Facility updateFacility(Facility newFacility, Facility oldFacility) {
oldFacility.setSomething(newFacility.getSomething());
// etc for the rest
}
I think you can apply Builder Pattern to resolve the issue, it may help you remove the frustration in the loop of if statement. Please see this link for more detials
You can override hashCode and equals methods in Facility class and do as follows:
if(!newFacility.equals(oldFacility))
{
//only when something is changed in newFacility, this condition will be excecuted
oldFacility = newFacility;
}
return oldFacility;
//This is just and example, you can return newFacility directly
NOTE : You can include all params or only those which decide the uniqueness. Its up to you.
Hope this helps!
You could copy the fields for the oldFacility object that you don't want to modify to some other variables, then update the whole oldFacility object, and just replace the fields that you didn't want to change with the content stored in the other variables. i.e.
private Facility updateFacility(Facility newFacility, Facility oldFacility){
String contentNotToBeModified; // or whatever variable type
contentNotToBeModified = oldFacility.getCity();
// Do the same for all data that you want to keep
oldFacility = newFacility;
newFacility.setCity(contentNotToBeModified);
}
So copy the data that you want to keep out of oldFacility first, then substitute oldFacility for newFacility, and replace the required attributes of newFacility with the data from oldFacility.
The not null check seems pointless to me since the NullPointerException won't be thrown if you slightly modify your example like this:
private Facility updateFacility(Facility newFacility, Facility oldFacility) {
if (newFacility != null) {
oldFacility.setCity(newFacility.getCity());
oldFacility.setContactEmail(newFacility.getContactEmail());
oldFacility.setContactFax(newFacility.getContactFax());
oldFacility.setContactName(newFacility.getContactName());
...
}
This will assign null values to references which were referencing to nulls anyway and will not cause any issues.
Assuming you were doing something like newFacility.getCity().toString() then the checks would be useful.
You could use Java Reflection for avoiding that copy/paste/write-same-Problem:
public Facility updateFacility(Facility newFacility, Facility oldFacility)
{
String[] properties = {"City", "ContactEmail", "ContactFax", "ContactName"};
for(String prop : properties) {
try {
Method getter = Facility.class.getMethod("get"+prop);
Method setter = Facility.class.getMethod("set"+prop, getter.getReturnType());
Object newValue = getter.invoke(newFacility);
if (newValue != null)
setter.invoke(oldFacility, newValue);
} catch (NoSuchMethodException |
SecurityException |
IllegalAccessException |
InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
...
}
Now you can simple change the properties[] array when there are new properties in the Facility class which you want to update that way.
EDIT: If you use the return type of the getter method to find the setter method, it is not neccessary to assume that the properties of Facility are all of the same type.
CAVEATS: Be careful in method renaming! This code will lead to runtime errors if you rename or remove methods from the Facility class. If you have to possibility to change the code of the Facility class, you should consider using an annotation to indicate which properties should be updated.

Get a list of all TreeCell objects that currently exist in a TreeView

I know that TreeCell objects are generated dynamically by the TreeView using a cell factory.
Is there a way to get a list of all TreeCell objects that currently exist?
I suppose I could keep track of them by modifying the cell factory. As in, whenever I create a new cell add it to some list. But then I'm not sure how to remove cells from my list once the TreeView disposes them (because they went out of view).
My solution to this is to use weak references:
[A] weak reference is a reference that does not protect the referenced object from collection by a garbage collector[.]
So, add this to your controller:
private final Set<MyTreeCell> myTreeCells = Collections.newSetFromMap(new WeakHashMap<>());
And make your CellFactory look like this:
myTreeView.setCellFactory((treeItem) -> {
MyTreeCell c = new MyTreeCell(icf);
Platform.runLater(() -> myTreeCells.add(c));
return c;
});
Now, myTreeCells will always contain the currently existing TreeCells.
Update
There is another, quite ugly solution using reflection to get a List of TreeCells. Note that this List is – as far as I know – a snapshot of JavaFXs List of TreeCells at one point in time. It is not backed.
#SuppressWarnings({ "unchecked" })
private Set<MyTreeCell> getListOfTreeCells() {
try {
final Field f = VirtualContainerBase.class.getDeclaredField("flow");
f.setAccessible(true);
final Field g = VirtualFlow.class.getDeclaredField("cells");
g.setAccessible(true);
final Set<MyTreeCell> s = new HashSet<>();
s.addAll((ArrayLinkedList<MyTreeCell>) g
.get((f.get((myTreeView.skinProperty().get())))));
return s;
}
catch (NoSuchFieldException | SecurityException | IllegalArgumentException
| IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
treeView.lookupAll(".tree-cell");
This will return a Set of Nodes that can be cast to TreeCells.

Set deep property on bean, creating intermediary instances if needed

I'm using BeanUtils.setProperty to set deep properties on a bean.
Home home = new Home() ;
String path = "home.family.father.age";
Integer value = 40;
BeanUtils.setProperty(home, path, value);
// Does the same as home.getHome().getFamily().getFather().setAge(value);
// But stops on null (instead of throwing an NPE).
The behavior of BeanUtils is to do nothing if one of the intermediary properties is null. So for example in my case, home's family property is null, and nothing happens. If I do
family = new Family();
Then father will be null and I'd have to initialize it too. Obviously my real use case is more complex, with many dynamic properties (and also indexed ones).
Is there a way to tell BeanUtils to instantiate intermediate members ? I know that in general this is not possible (because the concrete type of a property may not be known). But in my case all properties have concrete types and are proper beans (with a public no-args constructor). So it would be possible.
I'd like to make sure there aren't already existing solutions (using BeanUtils or something else) for this before rolling my own.
I rolled my own. It only supports simple properties but I guess adding support for nested/mapped properties wouldn't be too hard.
Here is a gist in case anyone needs the same thing:
https://gist.github.com/ThomasGirard/7115693
And here's what the most important part looks like:
/** Mostly copy-pasted from {#link PropertyUtilsBean.setProperty}. */
public void initProperty(Object bean, String path) throws SecurityException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
// [...]
// If the component is null, initialize it
if (nestedBean == null) {
// There has to be a method get* matching this path segment
String methodName = "get" + StringUtils.capitalize(next);
Method m = bean.getClass().getMethod(methodName);
// The return type of this method is the object type we need to init.
Class<?> propType = m.getReturnType();
try {
// Since it's a bean it must have a no-arg public constructor
Object newInst = propType.newInstance();
PropertyUtils.setProperty(bean, next, newInst);
// Now we have something instead of null
nestedBean = newInst;
} catch (Exception e) {
throw new NestedNullException("Could not init property value for '" + path + "' on bean class '"
+ bean.getClass() + "'. Class: " + propType);
}
}
// [...]
}

Categories