com.ejisto.core.classloading.javassist.ObjectEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.ejisto.core.classloading.javassist.ObjectEditor.java

Source

/*
 * Ejisto, a powerful developer assistant
 *
 * Copyright (C) 2010-2013 Celestino Bellone
 *
 * Ejisto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ejisto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.ejisto.core.classloading.javassist;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
import javassist.expr.ExprEditor;
import javassist.expr.FieldAccess;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.util.Arrays;
import java.util.Optional;

import static com.ejisto.constants.StringConstants.EJISTO_CLASS_TRANSFORMER_CATEGORY;
import static java.lang.String.format;

public class ObjectEditor extends ExprEditor {
    private static final Logger logger = Logger.getLogger(EJISTO_CLASS_TRANSFORMER_CATEGORY.getValue());
    private static final String PREFIX = "ejisto$$";
    private final EjistoMethodFilter filter;

    public ObjectEditor(EjistoMethodFilter filter) {
        super();
        this.filter = filter;
    }

    /**
     * Edits a field access replacing the code
     * with a call to {@link PropertyManager#mockField(String, String, String, Class, Object)}}.
     *
     * @param f the "fieldAccess"
     */
    @Override
    public void edit(FieldAccess f) throws CannotCompileException {
        String fieldName = f.getFieldName();
        if (shouldBeEdited(f, fieldName) && editFieldReader(f)) {
            CtClass clazz = f.getEnclosingClass();
            final Optional<String> fieldMarker = getFieldMarker(fieldName);
            if (fieldMarker.isPresent()) {
                clazz.addField(CtField.make(format("private boolean %s = true;", fieldMarker.get()), clazz));
            }
        }
    }

    private boolean shouldBeEdited(FieldAccess f, String fieldName) {
        return !fieldName.startsWith(PREFIX) && f.isReader() && filter.isFieldHandled(fieldName);
    }

    private boolean editFieldReader(FieldAccess f) throws CannotCompileException {
        String fieldName = f.getFieldName();
        if (!f.where().getMethodInfo().isMethod()) {
            trace("skipping field [" + fieldName
                    + "] because current context is either a constructor or a static initializer");
            return false;
        }
        if (hasAlreadyBeenProcessed(f, fieldName)) {
            trace("skipping field [" + fieldName + "] because it has already been processed");
            return false;
        }
        trace("editing field [" + fieldName + "]");
        StringBuilder instruction = new StringBuilder(
                "{ $_ = $proceed($$); $_ = ($r) com.ejisto.core.classloading.javassist.PropertyManager#newRemoteInstance().mockField(");
        instruction.append("\"").append(filter.getContextPath()).append("\",");
        try {
            CtField ctField = f.getEnclosingClass().getDeclaredField(fieldName);
            if (ctField.getType().isPrimitive()) {
                instruction.append("\"").append(fieldName).append("\",").append("\"").append(f.getClassName())
                        .append("\", $_); }");
            } else {
                instruction.append("\"").append(fieldName).append("\",").append("\"").append(f.getClassName())
                        .append("\", $type, $_); }");
            }
            trace("modifying field access with expression [" + instruction.toString() + "]");
            f.replace(instruction.toString());
            trace("done");
            return true;
        } catch (NotFoundException e) {
            logger.error("not found error", e);
        }
        return false;
    }

    private void trace(String s) {
        if (logger.isTraceEnabled()) {
            logger.trace(s);
        }
    }

    private static Optional<String> getFieldMarker(String fieldName) {
        if (StringUtils.isBlank(fieldName)) {
            return Optional.empty();
        }
        return Optional.of(PREFIX + fieldName);
    }

    private static boolean hasAlreadyBeenProcessed(FieldAccess f, String fieldName) {
        Optional<String> fieldMarker = getFieldMarker(fieldName);
        return !fieldMarker.isPresent() || Arrays.stream(f.getEnclosingClass().getDeclaredFields())
                .anyMatch(field -> field.getName().equals(fieldMarker.get()));
    }
}