/* * Copyright (C) 2011 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.marshalling.rebind.util; import static org.jboss.errai.codegen.meta.MetaClassFactory.parameterizedAs; import static org.jboss.errai.codegen.meta.MetaClassFactory.typeParametersOf; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.jboss.errai.codegen.Statement; import org.jboss.errai.codegen.builder.BlockBuilder; import org.jboss.errai.codegen.builder.ClassStructureBuilder; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaClassFactory; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.MetaParameterizedType; import org.jboss.errai.codegen.meta.MetaType; import org.jboss.errai.codegen.util.GenUtil; import org.jboss.errai.codegen.util.If; import org.jboss.errai.codegen.util.Stmt; import org.jboss.errai.config.rebind.EnvUtil; import org.jboss.errai.marshalling.client.Marshalling; import org.jboss.errai.marshalling.client.api.Marshaller; /** * @author Mike Brock <cbrock@redhat.com> * @author Christian Sadilek <csadilek@redhat.com> */ public class MarshallingGenUtil { private static final String USE_STATIC_MARSHALLERS = "errai.marshalling.use_static_marshallers"; private static final String FORCE_STATIC_MARSHALLERS = "errai.marshalling.force_static_marshallers"; public static final String USE_SHORT_IMPL_NAMES = "errai.marshalling.short_names"; public static final String USE_VERY_SHORT_IMPL_NAMES = "errai.marshalling.very_short_names"; public static final String ARRAY_VAR_PREFIX = "arrayOf_"; public static final String ERRAI_DOLLARSIGN_REPLACEMENT = ".erraiD."; public static final String ERRAI_UNDERSCORE_REPLACEMENT = ".erraiU."; public static String getVarName(final MetaClass clazz) { return clazz.isArray() ? getArrayVarName(clazz.getOuterComponentType().getFullyQualifiedName()) + "_D" + GenUtil.getArrayDimensions(clazz) : getVarName(clazz.asBoxed().getFullyQualifiedName()); } public static String getVarName(final Class<?> clazz) { return getVarName(MetaClassFactory.get(clazz)); } public static String getArrayVarName(final String clazz) { return ARRAY_VAR_PREFIX + normalizeName(clazz); } public static String getVarName(final String clazz) { return normalizeName(clazz); } private static String normalizeName(final String sourceString) { String result = sourceString; result = StringUtils.replace(result, "_", ERRAI_UNDERSCORE_REPLACEMENT); result = StringUtils.replace(result, "$", ERRAI_DOLLARSIGN_REPLACEMENT); result = StringUtils.replace(result, ".", "_"); return result; } public static MetaMethod findGetterMethod(final MetaClass cls, final String key) { MetaMethod metaMethod = _findGetterMethod("get", cls, key); if (metaMethod != null) return metaMethod; metaMethod = _findGetterMethod("is", cls, key); return metaMethod; } private static MetaMethod _findGetterMethod(final String prefix, final MetaClass cls, String key) { key = (prefix + key).toUpperCase(); for (final MetaMethod m : cls.getDeclaredMethods()) { if (m.getName().toUpperCase().equals(key) && m.getParameters().length == 0) { return m; } } return null; } /** * Returns the element type of the given metaclass under the following conditions: * <ul> * <li>toType is a collection type * <li>toType has a single type parameter * <li>toType's type parameter is not a wildcard * <li>toType's type parameter is a non-abstract (concrete) type * <li>toType's type parameter is not java.lang.Object * </ul> * * @param toType * The type to check for a known concrete collection element type. * @return The concrete element type meeting all above-mentioned criteria, or null if one or more * of the criteria fails. */ public static MetaClass getConcreteCollectionElementType(final MetaClass toType) { if (toType.isAssignableTo(Collection.class)) { return getConcreteElementType(toType); } return null; } /** * Returns the element type of the given metaclass under the following conditions: * <ul> * <li>toType has a single type parameter * <li>toType's type parameter is not a wildcard * <li>toType's type parameter is a non-abstract (concrete) type * <li>toType's type parameter is not java.lang.Object * </ul> * * @param toType * The type to check for a known concrete collection element type. * @return The concrete element type meeting all above-mentioned criteria, or null if one or more * of the criteria fails. */ public static MetaClass getConcreteElementType(final MetaClass toType) { return getConcreteTypeParameter(toType, 0, 1); } /** * Returns the map key type of the given metaclass under the following conditions: * <ul> * <li>toType is a {@link Map} * <li>toType's key type is not a wildcard * <li>toType's key type is a non-abstract (concrete) type * </ul> * * @param toType * The type to check for a known concrete map key type. * @return The concrete map key type meeting all above-mentioned criteria, or null if one or more * of the criteria fails. */ public static MetaClass getConcreteMapKeyType(final MetaClass toType) { if (toType.isAssignableTo(Map.class)) { return getConcreteTypeParameter(toType, 0, 2); } return null; } /** * Returns the map value type of the given metaclass under the following conditions: * <ul> * <li>toType is a {@link Map} * <li>toType's value type is not a wildcard * <li>toType's value type is a non-abstract (concrete) type * </ul> * * @param toType * The type to check for a known concrete map key type. * @return The concrete map value type meeting all above-mentioned criteria, or null if one or * more of the criteria fails. */ public static MetaClass getConcreteMapValueType(final MetaClass toType) { if (toType.isAssignableTo(Map.class)) { return getConcreteTypeParameter(toType, 1, 2); } return null; } private static MetaClass getConcreteTypeParameter(final MetaClass toType, final int typeParamIndex, final int typeParamsSize) { if (toType.getParameterizedType() != null) { final MetaType[] typeParms = toType.getParameterizedType().getTypeParameters(); if (typeParms != null && typeParms.length == typeParamsSize) { MetaClass typeParameter = null; if (typeParms[typeParamIndex] instanceof MetaParameterizedType) { final MetaParameterizedType parameterizedTypeParemeter = (MetaParameterizedType) typeParms[typeParamIndex]; typeParameter = (MetaClass) parameterizedTypeParemeter.getRawType(); } else if (typeParms[typeParamIndex] instanceof MetaClass) { typeParameter = (MetaClass) typeParms[typeParamIndex]; } return typeParameter; } } return null; } public static Collection<MetaClass> getDefaultArrayMarshallers() { final List<MetaClass> l = new ArrayList<>(); l.add(MetaClassFactory.get(Object[].class)); l.add(MetaClassFactory.get(String[].class)); l.add(MetaClassFactory.get(int[].class)); l.add(MetaClassFactory.get(long[].class)); l.add(MetaClassFactory.get(double[].class)); l.add(MetaClassFactory.get(float[].class)); l.add(MetaClassFactory.get(short[].class)); l.add(MetaClassFactory.get(boolean[].class)); l.add(MetaClassFactory.get(byte[].class)); l.add(MetaClassFactory.get(char[].class)); l.add(MetaClassFactory.get(Integer[].class)); l.add(MetaClassFactory.get(Long[].class)); l.add(MetaClassFactory.get(Double[].class)); l.add(MetaClassFactory.get(Float[].class)); l.add(MetaClassFactory.get(Short[].class)); l.add(MetaClassFactory.get(Boolean[].class)); l.add(MetaClassFactory.get(Byte[].class)); l.add(MetaClassFactory.get(Character[].class)); return Collections.unmodifiableCollection(l); } public static boolean isUseStaticMarshallers() { if (isForceStaticMarshallers()) return true; if (EnvUtil.isDevMode() && !EnvUtil.isJUnitTest()) return false; if (System.getProperty(USE_STATIC_MARSHALLERS) != null) { return Boolean.getBoolean(USE_STATIC_MARSHALLERS); } final Map<String, String> frameworkProperties = EnvUtil.getEnvironmentConfig().getFrameworkProperties(); if (frameworkProperties.containsKey(USE_STATIC_MARSHALLERS)) { return "true".equals(frameworkProperties.get(USE_STATIC_MARSHALLERS)); } else { return !EnvUtil.isDevMode(); } } public static boolean isForceStaticMarshallers() { if (System.getProperty(FORCE_STATIC_MARSHALLERS) != null) { return Boolean.getBoolean(FORCE_STATIC_MARSHALLERS); } final Map<String, String> frameworkProperties = EnvUtil.getEnvironmentConfig().getFrameworkProperties(); if (frameworkProperties.containsKey(FORCE_STATIC_MARSHALLERS)) { return "true".equals(frameworkProperties.get(FORCE_STATIC_MARSHALLERS)); } else { return false; } } public static void ensureMarshallerFieldCreated(final ClassStructureBuilder<?> classStructureBuilder, final MetaClass marshallerForType, final MetaClass type, final BlockBuilder<?> initMethod) { ensureMarshallerFieldCreated(classStructureBuilder, marshallerForType, type, initMethod, null); } public static void ensureMarshallerFieldCreated(final ClassStructureBuilder<?> classStructureBuilder, final MetaClass marshallerForType, final MetaClass type, final BlockBuilder<?> initMethod, final Statement marshallerCreationCallback) { final String fieldName = MarshallingGenUtil.getVarName(type); if (classStructureBuilder.getClassDefinition().getField(fieldName) == null) { final Statement marshallerLookup = createMarshallerLookup(marshallerForType, type, marshallerCreationCallback); if (initMethod == null) { classStructureBuilder.privateField(fieldName, parameterizedAs(Marshaller.class, typeParametersOf(type.getErased().asBoxed()))) .initializesWith(marshallerLookup).finish(); } else { classStructureBuilder.privateField(fieldName, parameterizedAs(Marshaller.class, typeParametersOf(type.getErased().asBoxed()))) .initializesWith(Stmt.load(null)).finish(); initMethod.append( If.isNull(Stmt.loadVariable(fieldName)).append( Stmt.loadVariable(fieldName).assignValue(marshallerLookup)).finish()); } } } private static Statement createMarshallerLookup(final MetaClass marshallerForType, final MetaClass type, final Statement marshallerCreationCallback) { Statement marshallerLookup = null; if (type.equals(marshallerForType)) { marshallerLookup = Stmt.loadVariable("this"); } else if (marshallerCreationCallback == null) { marshallerLookup = Stmt.invokeStatic(Marshalling.class, "getMarshaller", Stmt.loadLiteral(type.asBoxed())); } else { marshallerLookup = Stmt.invokeStatic(Marshalling.class, "getMarshaller", Stmt.loadLiteral(type.asBoxed().asClass()), marshallerCreationCallback); } return marshallerLookup; } }