package com.francetelecom.rd.stubs.engine; /* * #%L * Matos * $Id:$ * $HeadURL:$ * %% * Copyright (C) 2008 - 2014 Orange SA * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; /** * A lot of utility functions to relocate names, find objects, etc. * @author Pierre Cregut * */ public class ReflexUtil { private static final String VALUE_FIELD = "value"; private static final String JAVA_LANG = "java.lang"; private static final int JAVA_LANG_LENGTH = JAVA_LANG.length() + 1; final private String prefix; final private Pattern antipattern; final private String annotationPackage; /** * Constructor * @param prefix the prefix added in front of the packageName * @param altPrefix another prefix when there are several classLoaders * @param annotationPackage the name of the package where the annotations are. */ public ReflexUtil(String prefix, String altPrefix, String annotationPackage) { this.prefix = prefix; antipattern = altPrefix == null ? Pattern.compile( prefix + "[./]") : Pattern.compile("(" + altPrefix + "|" + prefix + ")[./]"); this.annotationPackage = prefix + "." + annotationPackage; } /** * Remove the effect of the constant pool patcher. * @param s * @return */ public String restoreString(String s) { return antipattern.matcher(s).replaceAll(""); } /** * Is it a loaded class (handled) or a base class of the system ? * @param clazz the refexive version of the class. * @return true if a class to dump. */ public boolean handledClass(Class<?> clazz) { return clazz != null && clazz.getName() != null && clazz.getName().startsWith(prefix); } /** * A way to get rid of some annotations. * @param annot The annotation considered * @return true if this annotation is invisible */ public boolean isStubAnnotation(Annotation annot) { return annotationPackage.equals(getPackageName(annot.annotationType())); } /** * Gives back the string of the package name where the class is defined. * Note: this has nothing to do with the notion of classloader Packages. * @param clazz * @return */ public static String getPackageName(final Class<?> clazz) { String classname = clazz.getName(); if (classname == null) return null; // Should not happen. It is not a canonical name int index = classname.lastIndexOf("."); return (index == -1) ? "" : classname.substring(0, index); } /** * Find an annotation by its name. It must be in the package of handled * annotation. (Do not forget that we use class relocation) * @param name the name of the annotation * @param annotArray the array of annotations. * @return the annotation if found or null. */ public Annotation findAnnotation(String name, Annotation [] annotArray) { for(Annotation annot : annotArray) { if(isStubAnnotation(annot)) { Class<?> annotClass = annot.getClass().getInterfaces()[0]; String shortName = annotClass.getSimpleName(); if (shortName.equals(name)) return annot; } } return null; } /** * Find annotations by their name. It must be in the package of handled * annotation. (Do not forget that we use class relocation). This version is useful * when there are several annotations. * @param name the name of the annotation * @param annotArray the array of annotations. * @return return the list of annotations with the given name. */ public List <Annotation> findAnnotations(String name, String names, Annotation [] annotArray) { ArrayList <Annotation> result = new ArrayList<Annotation>(); for(Annotation annot : annotArray) { if(isStubAnnotation(annot)) { Class<?> annotClass = annot.getClass().getInterfaces()[0]; String shortName = annotClass.getSimpleName(); if (shortName.equals(name)) result.add(annot); if (shortName.equals(names)) { Annotation [] contents = (Annotation []) getAnnotationField(annot, VALUE_FIELD); for (Annotation a : contents) { result.add(a); } } } } return result; } /** * Access to the value of a field in an annotation using reflection. * @param annotation the annotation object * @param field the name of the field. In fact implemented as a method. * @return object. The result must then be coerced. */ public static Object getAnnotationField(Annotation annotation, String field) { Class<?> annotClass = annotation.getClass(); try { Method getValue = annotClass.getMethod(field); return getValue.invoke(annotation); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Access the value field of annotation when it is an array of string. * @param annot * @return */ public static String [] getStringValues(Annotation annot) { Object contents = getAnnotationField(annot, VALUE_FIELD); if (contents instanceof String[]) return (String []) contents; if (contents instanceof String) return new String [] { (String) contents }; else return new String[0]; } /** * Access the value field of annotation when it is a string. * @param annot * @return */ public static String getStringValue(Annotation annot) { Object contents = getAnnotationField(annot, VALUE_FIELD); if (contents instanceof String) return (String) contents; else return null; } /** * Output a string that is roughly similar to what is printed in a source and * not internal format. * @param elt * @return */ public static String readableName(Class<?> elt) { if (elt.isArray()) return readableName(elt.getComponentType()) + "[]"; else return elt.getName().replace("$", "."); } /** * Same as a readable name but internal class keep the dollar sign in their name * @param elt * @return */ public static String readableSootName(Class<?> elt) { if (elt.isArray()) return readableSootName(elt.getComponentType()) + "[]"; else return elt.getName(); } /** * Combines making the output readable, getting rid of prefix and shortening * types in current package or java.lang * @param arg * @return */ public String prettyName(Class<?> arg, Class<?> here) { String pkg = getPackageName(here); String readable = readableName(arg); if (pkg != null && readable.startsWith(pkg)) readable = readable.substring(pkg.length() + 1); else if (readable.startsWith(JAVA_LANG)) readable = readable.substring(JAVA_LANG_LENGTH); return restoreString(readable); } /** * Gives back the annotation package * @return */ public String annotationPackage() { return restoreString(annotationPackage); } }