/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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. */ package org.jboss.errai.codegen.util; import java.util.HashMap; import java.util.Map; import org.jboss.errai.codegen.Modifier; import org.jboss.errai.codegen.builder.ClassStructureBuilder; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaConstructor; import org.jboss.errai.codegen.meta.MetaField; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.MetaParameter; /** * Utility class with methods that generate code to access private, default * access ("package private"), and protected methods and fields in arbitrary * classes. Each generator allows the choice of generating Java Reflection code * (for use on the server side) or JSNI code (for use on the client side). * * @author Mike Brock * @author Jonathan Fuerth * @author Christian Sadilek <csadilek@redhat.com> */ public class PrivateAccessUtil { private static final Map<String, PrivateMemberAccessor> PRIVATE_MEMBER_ACCESSORS = new HashMap<String, PrivateMemberAccessor>(); static { PRIVATE_MEMBER_ACCESSORS.put("reflection", new ReflectionPrivateMemberAccessor()); } public static void registerPrivateMemberAccessor(final String type, final PrivateMemberAccessor accessor) { PRIVATE_MEMBER_ACCESSORS.put(type, accessor); } public static void addPrivateAccessStubs(final String type, final ClassStructureBuilder<?> classBuilder, final MetaField f) { addPrivateAccessStubs(PrivateAccessType.Both, type, classBuilder, f); } /** * Generates methods for accessing a private field using either JSNI or Java * Reflection. The generated methods will be private and static. * * @param accessType * Whether to generate a read method, a write method, or both. * @param type * The type of accessors to use (ie. "reflection" or "jsni"). * @param classBuilder * The class builder to add the generated methods to. * @param f * The field the generated accessors read and write. */ public static void addPrivateAccessStubs(final PrivateAccessType accessType, final String type, final ClassStructureBuilder<?> classBuilder, final MetaField f) { addPrivateAccessStubs(accessType, type, classBuilder, f, new Modifier[]{Modifier.Static}); } /** * Generates methods for accessing a private field using either JSNI or Java * Reflection. The generated methods will be private. * * @param accessType * Whether to generate a read method, a write method, or both. * @param accessorType * The type of accessors to use (ie. "reflection" or "jsni"). * @param classBuilder * The class builder to add the generated methods to. * @param f * The field the generated accessors read and write. * @param modifiers * The modifiers on the generated method, for example * {@link Modifier#Final} or {@link Modifier#Synchronized}. <i>Never * specify {@code Modifier.JSNI}</i>; it is added automatically when * needed. */ public static void addPrivateAccessStubs(final PrivateAccessType accessType, final String accessorType, final ClassStructureBuilder<?> classBuilder, final MetaField f, Modifier[] modifiers) { final MetaClass type = f.getType(); final boolean read = accessType == PrivateAccessType.Read || accessType == PrivateAccessType.Both; final boolean write = accessType == PrivateAccessType.Write || accessType == PrivateAccessType.Both; final PrivateMemberAccessor privateMemberAccessor = PRIVATE_MEMBER_ACCESSORS.get(accessorType); if (privateMemberAccessor == null) { throw new IllegalArgumentException("unknown accessor type: " + accessorType); } if (read) { privateMemberAccessor.createReadableField(type, classBuilder, f, modifiers); } if (write) { privateMemberAccessor.createWritableField(type, classBuilder, f, modifiers); } } /** * Generates methods for accessing a nonpublic constructor using either JSNI or * Java Reflection. The generated method will be private and static. The name * of the generated method can be discovered by calling * {@link #getPrivateMethodName(MetaMethod)}. * * @param accessorType * The type of accessors to use (ie. "reflection" or "jsni"). * @param classBuilder * The class builder to add the generated method to. * @param m * The constructor the generated method will invoke */ public static void addPrivateAccessStubs(final String accessorType, final ClassStructureBuilder<?> classBuilder, final MetaConstructor m) { final PrivateMemberAccessor privateMemberAccessor = PRIVATE_MEMBER_ACCESSORS.get(accessorType); if (privateMemberAccessor == null) { throw new IllegalArgumentException("unknown accessor type: " + accessorType); } privateMemberAccessor.makeConstructorAccessible(classBuilder, m); } /** * Generates methods for accessing a nonpublic method using either JSNI or * Java Reflection. The generated method will be private and static. The name * of the generated method can be discovered by calling * {@link #getPrivateMethodName(MetaMethod)}. * * @param accessorType * The type of accessors to use (ie. "reflection" or "jsni"). * @param classBuilder * The class builder to add the generated method to. * @param m * The nonpublic method the generated method will invoke */ public static void addPrivateAccessStubs(final String accessorType, final ClassStructureBuilder<?> classBuilder, final MetaMethod m) { addPrivateAccessStubs(accessorType, classBuilder, m, new Modifier[]{Modifier.Static}); } /** * Generates methods for accessing a nonpublic method using either JSNI or Java * Reflection. The generated method will be private and static. * * @param accessorType * The type of accessors to use (ie. "reflection" or "jsni"). * @param classBuilder * The class builder to add the generated method to. * @param m * The method the generated accessors read and write. * @param modifiers * The modifiers on the generated method, for example * {@link Modifier#Final} or {@link Modifier#Synchronized}. <i>Never * specify {@code Modifier.JSNI}</i>; it is added automatically when * needed. */ public static void addPrivateAccessStubs(final String accessorType, final ClassStructureBuilder<?> classBuilder, final MetaMethod m, final Modifier[] modifiers) { final PrivateMemberAccessor privateMemberAccessor = PRIVATE_MEMBER_ACCESSORS.get(accessorType); if (privateMemberAccessor == null) { throw new IllegalArgumentException("unknown accessor type: " + accessorType); } privateMemberAccessor.makeMethodAccessible(classBuilder, m, modifiers); } public static String getPrivateFieldAccessorName(final MetaField field) { return field.getDeclaringClass().getName() + "_" + getTypeName(field.getType()) + "_" + field.getName(); } public static String getPrivateMethodName(final MetaMethod method) { final MetaClass declaringClass = method.getDeclaringClass(); String name = declaringClass.getName() + "_" + method.getName(); if (method.getParameters() != null) { for (final MetaParameter p : method.getParameters()) { name += "_" + getTypeName(p.getType()); } } return name; } private static String getTypeName(final MetaClass type) { if (type.isArray()) { return type.getName().replace("[]", "_array"); } else { return type.getName(); } } public static String condensify(String name) { return name.replace('.', '_').replace('$', '_'); } }