su.opencode.shuffler.RenamingVisitor.java Source code

Java tutorial

Introduction

Here is the source code for su.opencode.shuffler.RenamingVisitor.java

Source

/*
Shuffler is a plugin for IntelliJ Idea Community Edition,
that performs non-destructive java source code obfuscation.
Copyright (C) 2015 LLC "Open Code" http://www.o-code.ru
    
This program 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.
    
This program 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 su.opencode.shuffler;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Table;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.refactoring.JavaRenameRefactoring;
import com.intellij.refactoring.SilentJavaRenameRefactoring;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;

public class RenamingVisitor extends JavaRecursiveElementWalkingVisitor {

    private static int REFACTORING_ATTEMPTS = 5;
    private static final Set<String> JAVA_KEYWORDS;

    static {
        Set<String> keywords = new HashSet<String>();
        keywords.add("");
        keywords.add("default");
        for (Field f : PsiKeyword.class.getDeclaredFields()) {
            try {
                keywords.add((String) f.get(null));
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
        JAVA_KEYWORDS = Collections.unmodifiableSet(keywords);
    }

    private Table<String, String, Double> variableChains;
    private Table<String, String, Double> classChains;
    private Table<String, String, Double> methodChains;

    private boolean renamePrivate = true;
    private boolean renameProtected = true;
    private boolean renamePackage = false;
    private boolean renameDefault = false;
    private boolean renamePublic = false;

    private Set<String> ignoreMarkerAnnotations = new HashSet<String>();

    private CacheLoader<String, Boolean> markerCacheLoader = new CacheLoader() {
        @Override
        public Object load(Object o) throws Exception {
            return isIgnoreMarker((String) o);
        }
    };
    private LoadingCache<String, Boolean> ignoreMarkerFlagsCache = CacheBuilder.newBuilder()
            .build(markerCacheLoader);

    public RenamingVisitor(Table<String, String, Double> variableChains, Table<String, String, Double> classChains,
            Table<String, String, Double> methodChains) {
        Validate.notNull(classChains);
        Validate.notNull(methodChains);
        Validate.notNull(variableChains);
        this.classChains = classChains;
        this.methodChains = methodChains;
        this.variableChains = variableChains;

        Set<String> ignoreMarkers = new HashSet<String>();
        ignoreMarkers.add("javax.persistence.*");
        ignoreMarkers.add("javax.xml.bind.*");
        ignoreMarkers.add("org.hibernate.*");
        ignoreMarkers.add("org.codehaus.jackson.*");
        ignoreMarkers.add("com.fasterxml.jackson.*");
        ignoreMarkers.add("org.springframework.beans.factory.annotation.Autowired");
        this.ignoreMarkerAnnotations = Collections.unmodifiableSet(ignoreMarkers);
    }

    public RenamingVisitor(MarkovBuildingVisitor markovBuilder) {
        this(markovBuilder.getVariableTable(), markovBuilder.getClassTable(), markovBuilder.getMethodTable());
    }

    protected boolean refactor(final PsiElement element, final String newName, final boolean checkNonJava) {

        final JavaRenameRefactoring refactoring = new SilentJavaRenameRefactoring(element.getProject(), element,
                newName, checkNonJava);
        refactoring.run();

        return true;
    }

    private void processElement(PsiElement element) {
        if (element == null || !(element instanceof PsiModifierListOwner))
            return;
        PsiModifierListOwner el = (PsiModifierListOwner) element;

        if (ignoreElement(el)) {
            return;
        }

        int attempts = REFACTORING_ATTEMPTS;
        boolean refactored = false;

        String oldName = ((PsiNamedElement) element).getName();

        while (!refactored && attempts > 0) {
            String newName = null;
            do {
                attempts--;
                newName = generateName(element);
            } while (attempts > 0 && (JAVA_KEYWORDS.contains(newName.toLowerCase()) || StringUtils.isBlank(newName)
                    || oldName.equals(newName)));

            if (attempts > 0 && !StringUtils.isBlank(newName)) {
                refactored = refactor(element, newName, false);
            }
        }
    }

    protected boolean ignoreElement(PsiModifierListOwner element) {
        if (element == null)
            return true;
        if (!element.isWritable())
            return true;
        if (!element.isPhysical())
            return true;
        if (element instanceof PsiTypeParameter)
            return true;
        if (element instanceof PsiMethod
                && (((PsiMethod) element).isConstructor() || !isMethodDeclaration((PsiMethod) element)))
            return true;
        if (!(element instanceof PsiNamedElement) || ((PsiNamedElement) element).getName() == null)
            return true;
        if (element.getOriginalElement() != element)
            return true;
        if (element instanceof PsiLocalVariable)
            return false;

        return isOverride(element) || !renamePrivate && isPrivate(element)
                || !renameProtected && isProtected(element) || !renamePackage && isPackage(element)
                || !renameDefault && isDefault(element) || !renamePublic && isPublic(element)
                || ignoreMarkerPresent(element) || isSerializable(element);
    }

    protected String generateName(PsiElement element) {
        Table<String, String, Double> chainTable = null;
        if (element instanceof PsiVariable) {
            chainTable = variableChains;
        } else if (element instanceof PsiClass) {
            chainTable = classChains;
        } else if (element instanceof PsiMethod) {
            chainTable = methodChains;
        } else {
            throw new IllegalArgumentException();
        }

        List<String> name = generateNameList(chainTable);

        return conventionalizeName(element, name);
    }

    protected List<String> generateNameList(Table<String, String, Double> chainTable) {
        List<String> result = new ArrayList<String>();
        String current = "";
        while (result.isEmpty() || !"".equals(current)) {
            Map<String, Double> probs = chainTable.row(current);
            if (probs.isEmpty()) {
                current = "";
            } else {
                Iterator<Map.Entry<String, Double>> i = probs.entrySet().iterator();
                double rnd = ThreadLocalRandom.current().nextDouble();

                double sum = 0;
                Map.Entry<String, Double> e = null;
                while (i.hasNext() && rnd > sum) {
                    e = i.next();
                    sum += e.getValue();
                }
                current = e == null ? "" : e.getKey();
                result.add(current);
            }
        }
        return result;
    }

    protected String conventionalizeName(PsiElement element, List<String> name) {
        if (element instanceof PsiMethod) {
            return localName(name);
        } else if (element instanceof PsiClass) {
            return className(name);
        } else if (element instanceof PsiVariable) {
            PsiVariable var = (PsiVariable) element;
            if (var.hasModifierProperty("static") && var.hasModifierProperty("final")) {
                return constantName(name);
            } else {
                return localName(name);
            }
        }
        return null;
    }

    protected String className(List<String> name) {
        StringBuilder sb = new StringBuilder();
        for (String part : name) {
            if (StringUtils.isBlank(part))
                continue;
            sb.append(StringUtils.capitalize(StringUtils.lowerCase(part)));
        }
        return sb.toString();
    }

    protected String localName(List<String> name) {
        StringBuilder sb = new StringBuilder();
        for (String part : name) {
            if (StringUtils.isBlank(part))
                continue;
            sb.append(StringUtils.capitalize(StringUtils.lowerCase(part)));
        }
        char first = sb.charAt(0);
        first = Character.toLowerCase(first);
        sb.deleteCharAt(0);
        sb.insert(0, first);
        return sb.toString();
    }

    protected String constantName(List<String> name) {
        StringBuilder sb = new StringBuilder();
        for (String part : name) {
            if (StringUtils.isBlank(part))
                continue;
            sb.append("_").append(StringUtils.upperCase(part));
        }
        sb.deleteCharAt(0);
        return sb.toString();
    }

    protected boolean isOverride(PsiModifierListOwner element) {
        if (!(element instanceof PsiMethod)) {
            return false;
        }
        PsiMethod method = (PsiMethod) element;
        List<HierarchicalMethodSignature> supers = method.getHierarchicalMethodSignature().getSuperSignatures();
        return supers != null && supers.size() > 0;
    }

    protected boolean isPublic(PsiModifierListOwner element) {
        if (element.hasModifierProperty(PsiModifier.PUBLIC)) {
            return true;
        }
        if (element.hasModifierProperty(PsiModifier.PRIVATE)) {
            return false;
        }
        if (element instanceof PsiMethod) {
            Iterator<PsiMethod> i = OverridingMethodsSearch.search((PsiMethod) element).iterator();
            while (i.hasNext()) {
                PsiMethod method = i.next();
                if (method.hasModifierProperty(PsiModifier.PUBLIC)) {
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean isPrivate(PsiModifierListOwner element) {
        return element.hasModifierProperty(PsiModifier.PRIVATE);
    }

    protected boolean isProtected(PsiModifierListOwner element) {
        return element.hasModifierProperty(PsiModifier.PROTECTED);
    }

    protected boolean isPackage(PsiModifierListOwner element) {
        return element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) && !(element instanceof PsiLocalVariable);
    }

    protected boolean isDefault(PsiModifierListOwner element) {
        return element.hasModifierProperty(PsiModifier.DEFAULT) && !(element instanceof PsiLocalVariable);
    }

    protected boolean isSerializable(PsiModifierListOwner element) {
        if (element instanceof PsiField) {
            PsiClass[] supers = ((PsiField) element).getContainingClass().getSupers();
            for (PsiClass sup : supers) {
                if (sup.getQualifiedName().equals(Serializable.class.getName())) {
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean ignoreMarkerPresent(PsiModifierListOwner element) {
        if (element == null)
            return false;
        PsiModifierList modifierList = element.getModifierList();

        if (modifierList != null && modifierList.getChildren() != null) {
            for (PsiElement mod : modifierList.getChildren()) {
                if (!(mod instanceof PsiAnnotation))
                    continue;
                PsiAnnotation annotation = (PsiAnnotation) mod;
                try {
                    if (ignoreMarkerFlagsCache.get(annotation.getQualifiedName())) {
                        return true;
                    }
                } catch (ExecutionException ex) {

                    return true;
                }
            }
        }

        if (element instanceof PsiMethod) {
            PsiMethod method = (PsiMethod) element;
            HierarchicalMethodSignature signature = method.getHierarchicalMethodSignature();
            List<HierarchicalMethodSignature> signatureList = signature != null ? signature.getSuperSignatures()
                    : (List) Collections.emptyList();
            for (HierarchicalMethodSignature parent : signatureList) {
                if (ignoreMarkerPresent(parent.getMethod())) {
                    return true;
                }
            }
        }

        if (element instanceof PsiClass) {
            PsiClass clazz = (PsiClass) element;
            for (PsiClass c : clazz.getSupers()) {
                if (ignoreMarkerPresent(c)) {
                    return true;
                }
            }
        }

        PsiElement parent = element.getParent();
        while (parent != null && !(parent instanceof PsiModifierListOwner)) {
            parent = parent.getParent();
            if (parent instanceof PsiFileSystemItem) {
                parent = null;
            }
        }
        return ignoreMarkerPresent((PsiModifierListOwner) parent);
    }

    protected boolean isIgnoreMarker(String name) {
        if (ignoreMarkerAnnotations.contains(name))
            return true;
        String[] nameParts = name.split("\\.");
        StringBuilder sb = new StringBuilder();
        for (String part : nameParts) {
            if (ignoreMarkerAnnotations.contains(sb.append(part).append(".").toString() + "*")) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void visitVariable(PsiVariable element) {
        super.visitVariable(element);
        processElement(element);
    }

    @Override
    public void visitClass(PsiClass element) {
        super.visitClass(element);
        processElement(element);
    }

    @Override
    public void visitMethod(PsiMethod element) {
        super.visitMethod(element);
        processElement(element);
    }

    public boolean isRenamePrivate() {
        return renamePrivate;
    }

    public void setRenamePrivate(boolean renamePrivate) {
        this.renamePrivate = renamePrivate;
    }

    public boolean isRenameProtected() {
        return renameProtected;
    }

    public void setRenameProtected(boolean renameProtected) {
        this.renameProtected = renameProtected;
    }

    public boolean isRenamePublic() {
        return renamePublic;
    }

    public void setRenamePublic(boolean renamePublic) {
        this.renamePublic = renamePublic;
    }

    public boolean isRenamePackage() {
        return renamePackage;
    }

    public void setRenamePackage(boolean renamePackage) {
        this.renamePackage = renamePackage;
    }

    public boolean isRenameDefault() {
        return renameDefault;
    }

    public void setRenameDefault(boolean renameDefault) {
        this.renameDefault = renameDefault;
    }

    private static boolean isMethodDeclaration(PsiMethod element) {
        if (element == null)
            return false;
        PsiMethod[] sups = ShuffleAction.findDeepestSuperMethods(element);
        if (sups == null || sups.length == 0)
            return true;
        for (PsiMethod sup : sups) {
            if (sup.isEquivalentTo(element)) {
                return true;
            }
        }
        return false;
    }

}