All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.umd.cs.findbugs.ba.generic.GenericUtilities Maven / Gradle / Ivy

The newest version!
/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.ba.generic;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.CheckForNull;

import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;

import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.type.NullType;
import edu.umd.cs.findbugs.util.Util;

/**
 * Utilities for adding support for generics. Most of these methods can be
 * applied to generic and non generic type information.
 *
 * @author Nat Ayewah
 */
public class GenericUtilities {

    public static enum TypeCategory {

        /** A simple (non-generic ObjectType) */
        PLAIN_OBJECT_TYPE {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return obj;
            }

            @Override
            public String asString(GenericObjectType obj) {
                // obj.getTypeCategory() does not return PLAIN_OBJECT_TYPE
                return GenericUtilities.getString(obj);
            }
        },

        /** A array */
        ARRAY_TYPE {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return obj;
            }

            @Override
            public String asString(GenericObjectType obj) {
                // obj.getTypeCategory() does not return ARRAY_TYPE
                return GenericUtilities.getString(obj);
            }
        },

        /** A parameterized class e.g. List<String> */
        PARAMETERIZED {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return obj;
            }

            @Override
            public String asString(GenericObjectType obj) {
                StringBuilder b = new StringBuilder(obj.toPlainString());
                b.append("<");
                boolean first = true;
                for (Type t : obj.parameters) {
                    if (!first) {
                        b.append(",");
                    }
                    first = false;
                    b.append(GenericUtilities.getString(t));
                }
                b.append(">");
                return b.toString();
            }
        },

        /**
         * A simple type variable e.g. E. Underlying ObjectType is
         * java.lang.Object
         */
        TYPE_VARIABLE {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return Type.OBJECT;
            }

            @Override
            public String asString(GenericObjectType obj) {
                return obj.variable;
            }
        },

        /**
         * A simple wildcard i.e. ?. Underlying ObjectType is
         * java.lang.Object
         */
        WILDCARD {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return Type.OBJECT;
            }

            @Override
            public String asString(GenericObjectType obj) {
                return "?";
            }
        },

        /**
         * A wildcard that extends another ObjectType e.g.
         * ? extends Comparable. Underlying ObjectType is
         * java.lang.Object. The extended type can be an ObjectType
         * or a GenericObjectType
         */
        WILDCARD_EXTENDS {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return obj.extension;
            }

            @Override
            public String asString(GenericObjectType obj) {
                Type extension = obj.extension;
                assert extension != null;
                return "? extends " + GenericUtilities.getString(extension);
            }
        },

        /**
         * A wildcard that is extended by another ObjectType e.g.
         * ? super Comparable. Underlying ObjectType is
         * java.lang.Object. The super type can be an ObjectType or
         * a GenericObjectType
         */
        WILDCARD_SUPER {
            @Override
            public ReferenceType produce(GenericObjectType obj) {
                return Type.OBJECT;
            }

            @Override
            public String asString(GenericObjectType obj) {
                Type extension = obj.extension;
                assert extension != null;
                return "? super " + GenericUtilities.getString(extension);
            }
        };

        public abstract String asString(GenericObjectType obj);

        public abstract ReferenceType produce(GenericObjectType obj);

        public static String asString(ArrayType atype) {
            Type obj = atype.getBasicType();
            String result = GenericUtilities.getString(obj);
            return result + Util.repeat("[]", atype.getDimensions());
        }
    }

    /**
     * Get the TypeCategory that represents this Object
     *
     * @see GenericUtilities.TypeCategory
     */
    public static final TypeCategory getTypeCategory(Type type) {
        if (type instanceof GenericObjectType) {
            return ((GenericObjectType) type).getTypeCategory();
        }

        if (type instanceof ObjectType || type instanceof NullType) {
            return TypeCategory.PLAIN_OBJECT_TYPE;
        }

        if (type instanceof ArrayType) {
            return TypeCategory.ARRAY_TYPE;
        }

        throw new IllegalArgumentException("Not a reference type: " + type);
    }

    public static final boolean isPlainObject(Type type) {
        return getTypeCategory(type) == TypeCategory.PLAIN_OBJECT_TYPE;
    }

    /**
     * Get String representation of a Type including Generic information
     */
    public static final String getString(Type type) {
        if (type instanceof GenericObjectType) {
            return ((GenericObjectType) type).toString(true);
        } else if (type instanceof ArrayType) {
            return TypeCategory.asString((ArrayType) type);
        } else {
            return type.toString();
        }
    }

    static String stripAngleBrackets(String s) {
        if (s.indexOf('<') == -1) {
            return s;
        }
        StringBuilder result = new StringBuilder(s.length());
        int nesting = 0;
        boolean seenLeftBracket = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == '<') {
                nesting++;
                seenLeftBracket = true;
            } else if (c == '>') {
                nesting--;
            } else if (nesting == 0) {
                if (seenLeftBracket && c == '.') {
                    result.append('$');
                } else {
                    result.append(c);
                }
            }
        }
        return result.toString();
    }

    public static GenericObjectType getType(String className, List parameters) {
        return new GenericObjectType(className, parameters);
    }

    /**
     * This method is analogous to Type.getType(String), except
     * that it also accepts signatures with generic information. e.g.
     * Ljava/util/ArrayList<TT;>;
     * 

* * The signature should only contain one type. Use GenericSignatureParser to * break up a signature with many types or call createTypes(String) to * return a list of types */ public static @CheckForNull Type getType(String signature) { try { // ensure signature only has one type final Iterator signatureIterator = new GenericSignatureParser("(" + signature + ")V") .parameterSignatureIterator(); signature = signatureIterator.next(); if (signatureIterator.hasNext()) { throw new IllegalArgumentException("the following signature does not contain exactly one type: " + signature); } int index = 0; if (signature.startsWith("L")) { index = lastMatchedLeftAngleBracket(signature); if (index < 0) { return Type.getType(stripAngleBrackets(signature)); } String typeParameters = signature.substring(index + 1, nextUnmatchedRightAngleBracket(signature, index + 1)); List parameters = GenericUtilities.getTypeParameters(typeParameters); if (parameters == null) { return null; } String baseType = removeMatchedAngleBrackets(signature.substring(1, index)).replace('.', '$'); return new GenericObjectType(baseType, parameters); } else if (signature.startsWith("T")) { int i = signature.indexOf(';'); if (i > 0) { String var = signature.substring(1, i); if (var.indexOf('<') == -1) { return new GenericObjectType(var); } } // can't handle type variables return null; } else if (signature.startsWith("[")) { index++; while (signature.charAt(index) == '[') { index++; } Type componentType = getType(signature.substring(index)); if (componentType == null) { return null; } return new ArrayType(componentType, index); } else if (signature.startsWith("*")) { return new GenericObjectType("*"); } else if (signature.startsWith("+") || signature.startsWith("-")) { Type baseType = getType(signature.substring(1)); if (baseType == null) { return null; } return new GenericObjectType(signature.substring(0, 1), (ReferenceType) baseType); } else { // assert signature contains no generic information return Type.getType(signature); } } catch (IllegalStateException e) { AnalysisContext.logError("Error parsing signature " + signature, e); return null; } } public static ObjectType merge(@CheckForNull Type t1, ObjectType t2) { if (t1 instanceof GenericObjectType) { return merge((GenericObjectType) t1, t2); } return t2; } public static Type merge(@CheckForNull GenericObjectType t1, Type t2) { if (t1 == null) { return t2; } if (t2 instanceof ObjectType) { return merge(t1, (ObjectType) t2); } if (t2 instanceof NullType) { return t1; } return t2; } public static ObjectType merge(@CheckForNull GenericObjectType t1, ObjectType t2) { if (t1 == null || t2 instanceof GenericObjectType) { return t2; } List parameters = t1.getParameters(); if (parameters == null) { return t2; } return new GenericObjectType(t2.getClassName(), parameters); } public static String removeMatchedAngleBrackets(String s) { int first = s.indexOf('<'); if (first < 0) { return s; } StringBuilder result = new StringBuilder(s.substring(0, first)); int pos = first; int nesting = 0; while (pos < s.length()) { char c = s.charAt(pos++); if (c == '<') { nesting++; } else if (c == '>') { nesting--; } else if (nesting == 0) { result.append(c); } } return result.toString(); } public static int nextUnmatchedRightAngleBracket(String s, int startingAt) { int nesting = 0; int pos = startingAt; while (true) { if (pos < 0) { return -1; } char c = s.charAt(pos); if (c == '>') { if (nesting == 0) { return pos; } nesting--; } else if (c == '<') { nesting++; } pos++; } } public static int lastMatchedLeftAngleBracket(String s) { int nesting = 0; int pos = s.length() - 2; while (true) { if (pos < 0) { return -1; } char c = s.charAt(pos); if (c == '<') { nesting--; if (nesting == 0) { return pos; } } else if (c == '>') { nesting++; } else if (nesting == 0) { return -1; } pos--; } } /** * Parse a bytecode signature that has 1 or more (possibly generic) types * and return a list of the Types. * * @param signature * bytecode signature e.g. e.g. * Ljava/util/ArrayList<Ljava/lang/String;>;Ljava/util/ArrayList<TT;>;Ljava/util/ArrayList<*>; */ public static final @CheckForNull List getTypeParameters(String signature) { GenericSignatureParser parser = new GenericSignatureParser("(" + signature + ")V"); List types = new ArrayList<>(); Iterator iter = parser.parameterSignatureIterator(); while (iter.hasNext()) { String parameterString = iter.next(); ReferenceType t = (ReferenceType) getType(parameterString); if (t == null) { return null; } types.add(t); } return types; } public static final List split(String signature, boolean skipInitialAngleBracket) { List result = new ArrayList<>(); if (signature.charAt(0) != '<') { skipInitialAngleBracket = false; } int depth = 0; int start = 0; for (int pos = start; pos < signature.length(); pos++) { switch (signature.charAt(pos)) { case '<': depth++; break; case '>': depth--; if (depth == 0 && skipInitialAngleBracket) { skipInitialAngleBracket = false; start = pos + 1; } break; case ';': if (depth > 0) { break; } String substring = signature.substring(start, pos + 1); result.add(substring); start = pos + 1; break; default: break; } } if (depth != 0) { throw new IllegalArgumentException("Unbalanced signature: " + signature); } return result; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy