extending java.lang.String in groovy script

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);
        }
    }

}

Upvotes: 1

Views: 1366

Answers (1)

daggett
daggett

Reputation: 28564

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)

Upvotes: 1

Related Questions