i've a rather complex problem. I'am currently developing a a little groovy based script language for an ERP System. The functions and syntax of "my" script language are based on the already existing old FO language which is used by the erp system.
Therefore: I'am getting values from the ERP with calls like h.fieldname, where h the currently selected dataset is and fieldname the name of the field I want my field value from.
I get the field value e.g. of type String. What I now want is to extend these strings I retrieve with a few functions, which are based on the "old" syntax.
// some samples
// get last 3 characters
h.fieldname >> 3
// get first 4 characters
h.fieldname << 4
// should still work even if h.fieldname, returns something which extends String but is not a String
assert h.fieldname == "Foo"
UPDATE
I tried to make use of the answer of #daggett, here my approach:
public abstract class BaseScript extends Script implements GroovyObject {
#Override
public Object run() {
Object o = null;
try {
final ExpandoMetaClass metaClass = new ExpandoMetaClass(String.class, false, true);
//Closure c = { int x-> delegate[-x..-1] };
//ClosureMetaMethod foo = new ClosureMetaMethod​("rightShift ", c , doCall);
metaClass.initialize();
o = runCode();
} catch (Exception e) {
this.onerror(e);
} finally {
this.always();
}
return o;
}
public abstract Object runCode();
public Object always() {
return null;
}
public Object onerror(Object ex) {
if (ex instanceof Exception) {
Exception e = (Exception) ex;
e.printStackTrace();
}
return null;
}
}
But honestly i've no idea how to implement it and I also can't find any example.
UPDATE 2 and solution
Based on the answer of #daggett.
package groovy.runtime.metaclass.java.lang;
import groovy.lang.DelegatingMetaClass;
import groovy.lang.MetaClass;
public class StringMetaClass extends DelegatingMetaClass {
public StringMetaClass(Class<?> theClass) {
super(theClass);
}
public StringMetaClass(MetaClass metaClass) {
super(metaClass);
}
#Override
public Object invokeMethod(Object object, String name, Object[] args) {
// implementiert "test" >> 3
if (name.equals("rightShift")) {
if (args.length == 1) {
if (args[0] instanceof Integer) {
String str = object.toString();
int x = ((Integer) args[0]).intValue();
if (str.length() > x) {
return str.substring(str.length() - x);
}
return str;
} else {
throw new IllegalArgumentException("wrong argument type, should be integer");
}
} else {
throw new IllegalArgumentException("too many arguments");
}
}
// implementiert "test" << 3
if (name.equals("leftShift")) {
if (args.length == 1) {
if (args[0] instanceof Integer) {
String str = object.toString();
int x = ((Integer) args[0]).intValue();
if (str.length() > x) {
return str.substring(0,x);
}
return str;
} else {
throw new IllegalArgumentException("wrong argument type, should be integer");
}
} else {
throw new IllegalArgumentException("too many arguments");
}
}
else {
return super.invokeMethod(object, name, args);
}
}
}
you can't extend string class because it's final, however in groovy you can add new methods to string class with help of metaclass
String.metaClass.rightShift = { int x-> delegate[-x..-1] }
"1234567890" >> 3
returns
890
in the same way implement the method leftShift for <<
the last request (assert s1==s2) is not relevant because String is a final class (not extendable)
Related
I have a number of methods, each of which checks the same set of conditions and returns a null value if none of the conditions are met, otherwise returns an object of different classes.
Is there a way to not have to write all of these terms for each function and use less code?
public A methode1()
{
if ///something
return A("xxx")
else if ///something
return A("yyy")
else if ///something
return A("zzzz")
else
return Error() // or return null
}
public B methode2()
{
if ///something
return B("mmmm")
else if ///something
return B("nnn")
else if ///something
return B("bbbb")
else
return Error() // or return null
}
public C methode3()
{
if ///something
return C("oooo")
else if ///something
return C("ggg")
else if ///something
return C("llll")
else
return Error() // or return null
}
You can combine template method pattern with generics:
public abstract class AbstractTemplate<T>
{
public T methode()
{
if ///something
return Do1();
else if ///something
return Do2();
else if ///something
return Do3();
else
return Error() // or return null
}
protected abstract T Do1();
protected abstract T Do2();
protected abstract T Do3();
}
public class ConcreteATemplate : AbstractTemplate<A>
{
protected override T Do1() => A("xxx");
protected override T Do2() => A("yyy");
protected override T Do3() => A("zzzz");
}
And use it inside your methods:
public A methode1() => new ConcreteATemplate().methode(); // also can "cache" instance in your case in static readonly field.
Standard approch is , you can use a factory class with interface/abstract class
public interface IOutput {
}
public class Output1 : IOutput{
}
public class Output2 : IOutput{
}
public class MyFactory
{
public IOutput Get()// add args list of any
{
if(condition) // you can use args in condition if necessary
return new Output1();
else
return new Output2();
}
}
You can use generic method and repository pattern. Here is a generic method example with C#. I think it may give you some idea..
public static async Task<List<ObjectType>> GetDataList<ObjectType>(UserAuthorization token) where ObjectType : BaseModel
{
List<ObjectType> data = new List<ObjectType>();
try
{
if(token.UserTypes.Count > 0)
{
Type genericClass = typeof(Repository<>);
Type constructedClass = genericClass.MakeGenericType(typeof(ObjectType));
MERPDbContext mERPContext = new MERPDbContext();
var created = (Repository<ObjectType>)Activator.CreateInstance(constructedClass, mERPContext);
if (token.UserTypes[0].Id == (byte)UserTypes.SuperAdmin)
{
var sub = await created.GetAsync();
data = sub.ToList();
}
else if (token.UserTypes[0].Id == (byte)UserTypes.InstituteAdmin)
{
data = await created.FilterListAsync
(x => x.InstituteId == token.InstituteID);
}
else
{
data = await created.FilterListAsync(x =>
x.InstituteId == token.InstituteID && x.CampusId == token.CampusID);
}
}
}
catch (Exception e)
{
}
return data;
}
You could use a method with a generic type parameter and a set of initial values.
private T GetIfOkay<T>(string a, string b, string c)
where T : new()
{
if (something)
return new T(a);
else if (something else)
return new T(b);
else if (yet something else)
return new T(c);
else
return null;
}
public A methode1()
{
return GetIfOkay<A>("xxx", "yyy", "zzzz");
}
public B methode2()
{
return GetIfOkay<B>("mmmm", "nnn", "bbbb");
}
// etc.
If you need a more dynamic behaviour, #donggas90's solution.
See:
Generic Methods (C# Programming Guide)
C# Generics (TutorialsTeacher)
According to your comment, I tried to rewrite your sample, hope this will help you. ask freely if you need more clarity.
class Program
{
static void Main(string[] args)
{
var objFactory = new MyFactory();
// Get and cast return object
A a1= (A) objFactory.ConsolidatedMethod("Condition 1 for Objct A", "xxx");
// Or Directly assian to base object
IOutput a2 = objFactory.ConsolidatedMethod("Condition 2 for Objct A", "yyyy");
// use anonymous object
var b1= objFactory.ConsolidatedMethod("Condition 1 for Objct B", "mmmm");
var nullcheck1 = objFactory.ConsolidatedMethod("null conditionj", "i'm null");
}
}
interface IOutput
{
}
class A : IOutput
{
public A(string objParam)
{
}
}
class B : IOutput
{
public B(string objParam)
{
}
}
class NullOutput : IOutput
{
public NullOutput(string objParam)
{
}
}
class MyFactory
{
/// <summary>
/// Demo
/// </summary>
/// <param name="arg">you can use this based on your requirement </param>
/// <param name="objparam">you can use this based on your requirement </param>
/// <returns>IOutput</returns>
public IOutput ConsolidatedMethod(string arg, string objparam)
{
IOutput _output=default;
if (arg == "Condition 1 for Objct A")
_output = new A(objparam);
else if (arg == "Condition 2 for Objct A")
_output = new A(objparam);
else if (arg == "Condition 1 for Objct b")
_output = new B(objparam);
else if (arg == "Condition 2 for Objct B")
_output = new B(objparam);
else
_output = new NullOutput(objparam);
return _output;
}
}
i have the code like this when i create it like this
public final class PhpArray extends AbstractMap
{
private TreeMap t;
private HashMap m;
public PhpArray() {
this.t = new TreeMap(Request.PHP_ARRAY_KEY_COMPARATOR);
this.m = null;
}
#Override
public Object put(final Object key, final Object value) {
if (this.m != null) {
return this.m.put(key, value);
}
try {
return this.t.put(key, value);
}
catch (ClassCastException e) {
this.m = new HashMap(this.t);
this.t = null;
return this.m.put(key, value);
}
}
#Override
public Set entrySet() {
if (this.t != null) {
return this.t.entrySet();
}
return this.m.entrySet();
}
public int arraySize() {
if (this.t == null) {
throw new IllegalArgumentException("The passed PHP \"array\" is not a sequence but a dictionary");
}
if (this.t.size() == 0) {
return 0;
}
return 1 + this.t.lastKey();
}
}
but when i update my project i got error in the code
return 1 + this.t.lastKey();
the error is an arguments + is undefined.. why like that ? and how to fix the problem ?
TreeMap is a generic class but in the code in your question you have used it without type parameters. This means that this line of your code:
private TreeMap t;
is essentially this:
private TreeMap<Object, Object> t;
In other words t.lastKey() returns an Object and the operator + can't be used with Object because an Object is not a number.
Perhaps you meant to call method size() rather than method lastKey()?
Perhaps this tutorial will help?
I have a code that uses Field built in function in java and i could not find a way to replace it in c++ the code is shown below,
import java.lang.reflect.Field;
public class ParameterValue {
public String objectPath;
public Object objectReference;
public String fieldPath;
public String fieldPathNoCase;
public Field field;
public double value;
public ParameterValue(String path, ObjectTree tree, Field fieldInfo) {
objectPath = path;
objectReference = tree.getObject(path);
field = fieldInfo;
fieldPath = objectPath + "." + field.getName();
fieldPathNoCase = fieldPath.toLowerCase();
read();
}
public int getPrecision() {
if (field.getType().getName() == "float" || field.getType().getName() == "double")
return 2;
else
return 0;
}
public double getPrecisionMultiplier() {
return Math.pow(10, getPrecision());
}
public void read() {
String type = field.getType().getName();
try {
if (type.equals("double"))
value = field.getDouble(objectReference);
else if (type.equals("float"))
value = field.getFloat(objectReference);
else if (type.equals("int"))
value = field.getInt(objectReference);
else if (type.equals("byte"))
value = field.getByte(objectReference);
else
throw new RuntimeException();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
value = Math.round(value * getPrecisionMultiplier()) / getPrecisionMultiplier();
}
public void write() {
String type = field.getType().getName();
try {
if (type.equals("double"))
field.setDouble(objectReference, value);
else if (type.equals("float"))
field.setFloat(objectReference, (float)value);
else if (type.equals("int"))
field.setInt(objectReference, (int)Math.round(value));
else if (type.equals("byte"))
field.setByte(objectReference, (byte)Math.round(value));
else
throw new RuntimeException();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public void rebind(ObjectTree tree) {
objectReference = tree.getObject(objectPath);
}
}
What i have understood from the code is that i need to find a class that can convert the value in it to Double, Float,etc. I have looked for something that can do this but i was not able to do so.
reference of the code:
https://www.programcreek.com/java-api-examples/index.php?source_dir=SecugenPlugin-master/src/sourceafis/simple/Fingerprint.java#
As per my knowledge there is no equivalent class in C++. Now for your requirement first you list out what are all a java.lang.reflect.Field class provides in java. Once you listed all the utility methods, just sort list all methods that you really requires in your C++ application. Once done you do create a C++ class with the same name and methods types and implement the logic by yourself if possible.
Sometimes methods have the only difference somwhere in the middles of their bodies and it's difficult to generalize them or extract common part of code to a single method.
Question itself: How would you refactor the following implementations of interface methods to avoid duplicate code around for loop body?
interface MyInterface {
Integer myInterfaceMethod(String inputStr);
Integer myInterfaceOtherMethod(String inputStr)
}
class MyClass implements MyInterface {
public Integer myInterfaceMethod(String inputStr) {
#Override
try {
List<String> listDependingOnString = getListByString(inputStr);
Integer result = -1;
if (inputStr != null) {
result = 0;
for (String str : listDependingOnString) {
// Some different code, given just for example
result += str.length();
}
}
return result;
} catch (Exception e) {
exceptionProcessing(e);
return null;
}
}
#Override
public Integer myInterfaceOtherMethod(String inputStr) {
try {
List<String> listDependingOnString = getListByString(inputStr);
Integer result = -1;
if (inputStr != null) {
result = 0;
for (String str : listDependingOnString) {
// Some different code, given just for example
System.out.println(str);
++result;
}
}
return result;
} catch (Exception e) {
exceptionProcessing(e);
return null;
}
}
}
For this particular example, a lambda would work nicely:
private Integer computeStringFunction(String inputStr, BiFunction<Integer,String,Integer> accumulator) {
try {
List<String> listDependingOnString = getListByString(inputStr);
Integer result = -1;
if (inputStr != null) {
result = 0;
for (String str : listDependingOnString) {
result = accumulator.apply(result, str);
}
}
return result;
} catch (Exception e) {
exceptionProcessing(e);
return null;
}
public Integer myInterfaceMethod(String inputStr) {
return computeStringFunction(inputStr,
(Integer oldValue, String str) -> oldValue + str.length());
}
public Integer myInterfaceOtherMethod(String inputStr) {
return computeStringFunction(inputStr,
(Integer oldValue, String str) -> {
System.out.println(str);
return oldValue + 1;
});
}
"accumulator" here is a function that takes an integer and a string and returns another integer, and whose intent is to keep a "running total" of some sort.
BiFunction documentation
Note: not tested
The key to remove duplicate pattern in codes is to abstract the common part to one place and then find a way to pass the different part of "code pieces" as parameters to execute, for languages in which function is first class citizen(JavaScript, Python), you can always wrap the "code pieces" as functions. But it's not applicable for Java because method in Java is not a value, one way to resolve it is to define interfaces, and then pass the instance of a class which implements the interface as parameters, with lambda expression in Java 8 it can be more simpler.
Take the code in question as example, the common pattern is:
iterate the list and process each item
accumulate the result of each item and return
Then we can define two interfaces:
#FunctionalInterface
public interface ItemHandler<T, R> {
/**
* Takes input item of type T, then returns result of type R
*/
R handle(T t);
}
And another interface to accumulate the result:
#FunctionalInterface
public interface ItemResultAccumulator<T> {
T accumulate(T t1, T t2);
}
and then your code could be refactored as(I removed all exception handling and null checking code, to make the code less verbose to view):
public class MyClass implements MyInterface {
private static final ItemResultAccumulator<Integer> ADDER = (t1, t2) -> t1 + t2;
#Override
public Integer myInterfaceMethod(String inputStr) {
return processList(getListByString(inputStr), s -> s.length(), ADDER);
}
#Override
public Integer myInterfaceOtherMethod(String inputStr) {
return processList(getListByString(inputStr), s -> {
System.out.println(s);
return Integer.valueOf(1);
}, ADDER);
}
private Integer processList(List<String> list, ItemHandler<String, Integer> handler, ItemResultAccumulator<Integer> accumulator) {
Integer result = 0;
if (list != null && list.size() > 0) {
for (String item : list) {
result = accumulator.accumulate(result, handler.handle(item));
}
}
return result;
}
private List<String> getListByString(String inputStr) {
// Your logic to generate list by input
return Lists.newArrayList(inputStr.split(","));
}
}
This is a little of my thinking of this problem, hope this could be helpful:-)
I have two objects of same type.
Class A {
String a;
List b;
int c;
}
A obj1 = new A();
A obj2 = new A();
obj1 => {a = "hello"; b = null; c = 10}
obj2 => {a = null; b = new ArrayList(); c = default value}
Can you please let me know what is the best way to combine this objects into single object?
obj3 = {a = "hello"; b = (same arraylist from obj2); c = 10}
This works as long as you have POJOs with their own getters and setters. The method updates obj with non-null values from update. It calls setParameter() on obj with the return value of getParameter() on update:
public void merge(Object obj, Object update){
if(!obj.getClass().isAssignableFrom(update.getClass())){
return;
}
Method[] methods = obj.getClass().getMethods();
for(Method fromMethod: methods){
if(fromMethod.getDeclaringClass().equals(obj.getClass())
&& fromMethod.getName().startsWith("get")){
String fromName = fromMethod.getName();
String toName = fromName.replace("get", "set");
try {
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
Object value = fromMethod.invoke(update, (Object[])null);
if(value != null){
toMetod.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
I am using Spring Framework. I was facing the same issue on a project.
To solve it i used the class BeanUtils and the above method,
public static void copyProperties(Object source, Object target)
This is an example,
public class Model1 {
private String propertyA;
private String propertyB;
public Model1() {
this.propertyA = "";
this.propertyB = "";
}
public String getPropertyA() {
return this.propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA = propertyA;
}
public String getPropertyB() {
return this.propertyB;
}
public void setPropertyB(String propertyB) {
this.propertyB = propertyB;
}
}
public class Model2 {
private String propertyA;
public Model2() {
this.propertyA = "";
}
public String getPropertyA() {
return this.propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA = propertyA;
}
}
public class JustATest {
public void makeATest() {
// Initalize one model per class.
Model1 model1 = new Model1();
model1.setPropertyA("1a");
model1.setPropertyB("1b");
Model2 model2 = new Model2();
model2.setPropertyA("2a");
// Merge properties using BeanUtils class.
BeanUtils.copyProperties(model2, model1);
// The output.
System.out.println("Model1.propertyA:" + model1.getPropertyA(); //=> 2a
System.out.println("Model1.propertyB:" + model1.getPropertyB(); //=> 1b
}
}
Maybe something like
class A {
String a;
List<..> b;
int c;
public void merge(A other) {
this.a = other.a == null ? this.a : other.a;
this.b.addAll(other.b);
this.c = other.c == 0 ? this.c : other.c;
}
}
A a1 = new A();
A a2 = new A();
a1.a = "a prop";
a2.c = 34;
a1.merge(a2);
A.merge might return a new A object instead of modifing current.
Just accommodating boolean sync. and case sensitive(camel notation)
public boolean merge(Object obj){
if(this.equals(obj)){
return false;
}
if(!obj.getClass().isAssignableFrom(this.getClass())){
return false;
}
Method[] methods = obj.getClass().getMethods();
for(Method fromMethod: methods){
if(fromMethod.getDeclaringClass().equals(obj.getClass())
&& (fromMethod.getName().matches("^get[A-Z].*$")||fromMethod.getName().matches("^is[A-Z].*$"))){
String fromName = fromMethod.getName();
String toName ;
if(fromName.matches("^get[A-Z].*")){
toName = fromName.replace("get", "set");
}else{
toName = fromName.replace("is", "set");
}
try {
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
Object value = fromMethod.invoke(this, (Object[])null);
if(value != null){
toMetod.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
If you create getters and setters for the attributes, you can use the copyProperties method from Commons BeanUtils.
Add this method to your POJO, then use it like myObject.merge(newObject). It uses generics to loop through your POJO's fields, so you don't mention any field names:
/**
* Fill current object fields with new object values, ignoring new NULLs. Old values are overwritten.
*
* #param newObject Same type object with new values.
*/
public void merge(Object newObject) {
assert this.getClass().getName().equals(newObject.getClass().getName());
for (Field field : this.getClass().getDeclaredFields()) {
for (Field newField : newObject.getClass().getDeclaredFields()) {
if (field.getName().equals(newField.getName())) {
try {
field.set(
this,
newField.get(newObject) == null
? field.get(this)
: newField.get(newObject));
} catch (IllegalAccessException ignore) {
// Field update exception on final modifier and other cases.
}
}
}
}
}
There is a dynamic solution to merge any two objects which require Reflection and Recursion.
public <T> T merge(T local, T remote, ArrayList<String> listOfClass)
throws IllegalAccessException, InstantiationException {
Class<?> clazz = local.getClass();
Object merged = clazz.newInstance();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object localValue = field.get(local);
Object remoteValue = field.get(remote);
if (localValue != null) {
if (listOfClass.contains(localValue.getClass().getSimpleName())) {
field.set(merged, this.merge(localValue, remoteValue, listOfClass));
} else {
field.set(merged, (remoteValue != null) ? remoteValue : localValue);
}
} else if (remoteValue != null) {
field.set(merged, remoteValue);
}
}
return (T) merged;
}
Variable Description:
local: The object on to which the other will be merged
remote: The object which will be merged to the local object
listOfClass: The ArrayList of custom classes in the given object
The function returns a merged object which is good to go.
Kudos! :)
In your very special case it looks like you want a new object that takes the real values from both instances. Here is an implementation that will do that. The method should be add to class A so that it can access the fields.
public A specialMergeWith(A other) {
A result = new A();
result.a = (a == null ? other.a : a);
result.b = (b == null ? other.b : b);
result.c = (c == DEFAULT_VALUE ? other.c : c);
return result;
}
public static Object mergeObjects(Object source, Object target) throws Exception {
Field[] allFields = source.getClass().getDeclaredFields();
for (Field field : allFields) {
if(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())){
continue;
}
if (!field.isAccessible() && Modifier.isPrivate(field.getModifiers()))
field.setAccessible(true);
if (field.get(source) != null) {
field.set(target, field.get(source));
}
}
return target;
}
Using java reflection, support only for the same class.