~/home of geeks

Extending Commons-BeanUtils

· 522 Wörter · 3 Minute(n) Lesedauer

In some cases it’s usefull to concat the values of BeanProperties when using the BeanUtils from Apaches Commons. I just extended some few functionality to achiev this within the property-identifier itself.

With the help of a simple and weak parser one can use property descriptions like propertyA+' '+propertyB. For example with some Person bean one can write

ExtendedBeanUtil.getProperty(personBean, "lastname+', '+firstname");

The parser supports text within single quotes ' as well as + for concatenation. Also \\ within a text can be used to escape values, like\\\\' to escape a single quote (the first backslash is for the Java parser to escape the second backslash). Of course such extended property descriptions can only be used for display issues, not for setting values of beans.

package de.keyboardsamurais.narcanti.commons.beans;

import org.apache.commons.beanutils.BeanUtils;

/**
 * Supports concatenation and stringliterals within property descriptions.
 */
public class ExtendedBeanUtil {
    public static class Token {
        private boolean aProperty;
        private String value;

        public Token(boolean aProperty, String value) {
            this.aProperty = property;
            this.value = value;
        }

        public boolean isAProperty() {
            return aProperty;
        }

        public void setAProperty(boolean property) {
            aProperty = property;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }

    public static class TokenParser {
        private static enum State {Property, Concat, Text, Escape}

        private ArrayList<Token> tokens = new ArrayList<Token>();

        public TokenParser(String content) {
            State state = State.Property;
            final StringBuilder value = new StringBuilder();
            for (final char c : content.toCharArray()) {
                // System.out.println("c: \\""+c+"\\" cur. state: "+state);
                switch (state) {
                    case Property:
                        // end of a property, change to concatenation
                        if (c == '+') {
                            // only if there is a property name at all
                            if (value.length() > 0) {
                                tokens.add(new Token(true, value.toString()));
                                value.delete(0, value.length());
                            }
                            state = State.Concat;
                        }
                        // skip whitespaces wihtin property names
                        else if (!Character.isWhitespace(c)) {
                            value.append(c);
                        }
                        break;
                    case Concat:
                        // skip double concatenations (++) and whitespaces
                        if (c != '+' && !Character.isWhitespace(c)) {
                            // next comes a text
                            if (c == '\'') {
                                state = State.Text;
                            }
                            // as this is not a text, it must be a property
                            else {
                                value.append(c);
                                state = State.Property;
                            }
                        }
                        break;
                    case Text:
                        // escape some character
                        if (c == '\\') {
                            state = State.Escape;
                        }
                        // end of a string
                        else if (c == '\'') {
                            // only if it's not empty
                            if (value.length() > 0) {
                                tokens.add(new Token(false, value.toString()));
                                value.delete(0, value.length());
                            }
                            state = State.Property;
                        }
                        // append anything else (incl. whitespaces)
                        else {
                            value.append(c);
                        }
                        break;
                    case Escape:
                        // no matter which character, just append as it has been escaped
                        value.append(c);
                        state = State.Text;
                        break;
                }
            }
            // fini
            if (value.length() > 0) {
                switch (state) {
                    case Property:
                        tokens.add(new Token(true, value.toString()));
                        break;
                    case Text:
                        tokens.add(new Token(false, value.toString()));
                        break;
                }
            }
        }

        public List<Token> getTokens() {
            return tokens;
        }
    }

    private ExtendedBeanUtil() {}

    // this method does the work of buzilding a result string from the property
    public static String getProperty(Object targetBean, String property) {
        final TokenParser parser = new TokenParser(property);
        final StringBuilder result = new StringBuilder();
        for (final Token token : parser.getTokens()) {
            if (!token.isAProperty()) {
                result.append(token.getValue());
            } else {
                result.append(BeanUtils.getProperty(targetBean, token.getValue()));
            }
            return result.toString();
        }
    }
}