/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.isis.core.metamodel.facets; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Collections; import java.util.List; import com.google.common.collect.Lists; import org.apache.isis.applib.Identifier; import org.apache.isis.core.commons.lang.StringExtensions; import org.apache.isis.core.metamodel.facetapi.FacetUtil; import org.apache.isis.core.metamodel.facetapi.FeatureType; import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder; import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet; import org.apache.isis.core.metamodel.facets.collparam.semantics.CollectionSemanticsFacet; import org.apache.isis.core.metamodel.facets.collparam.semantics.CollectionSemanticsFacetDefault; import org.apache.isis.core.metamodel.specloader.*; /** * non-final only so it can be mocked if need be. */ public class FacetedMethod extends TypedHolderDefault implements IdentifiedHolder { // ////////////////////////////////////////////////// // Factory methods // ////////////////////////////////////////////////// /** * Principally for testing purposes. */ public static FacetedMethod createForProperty(final Class<?> declaringType, final String propertyName) { try { final Method method = declaringType.getMethod("get" + StringExtensions.asPascal(propertyName)); return FacetedMethod.createForProperty(declaringType, method); } catch (final SecurityException | NoSuchMethodException e) { throw new RuntimeException(e); } } /** * Principally for testing purposes. */ public static FacetedMethod createForCollection(final Class<?> declaringType, final String collectionName) { try { final Method method = declaringType.getMethod("get" + StringExtensions.asPascal(collectionName)); return FacetedMethod.createForCollection(declaringType, method); } catch (final SecurityException | NoSuchMethodException e) { throw new RuntimeException(e); } } /** * Principally for testing purposes. */ public static FacetedMethod createForAction( final Class<?> declaringType, final String actionName, final SpecificationLoader specificationLoader, final Class<?>... parameterTypes) { try { final Method method = declaringType.getMethod(actionName, parameterTypes); return FacetedMethod.createForAction(declaringType, method, specificationLoader); } catch (final SecurityException | NoSuchMethodException e) { throw new RuntimeException(e); } } public static FacetedMethod createForProperty(final Class<?> declaringType, final Method method) { return new FacetedMethod(FeatureType.PROPERTY, declaringType, method, method.getReturnType(), emptyParameterList()); } public static FacetedMethod createForCollection(final Class<?> declaringType, final Method method) { return new FacetedMethod(FeatureType.COLLECTION, declaringType, method, null, emptyParameterList()); } public static FacetedMethod createForAction( final Class<?> declaringType, final Method method, final SpecificationLoader specificationLoader) { return new FacetedMethod(FeatureType.ACTION, declaringType, method, method.getReturnType(), getParameters(declaringType, method, specificationLoader)); } private static List<FacetedMethodParameter> getParameters( final Class<?> declaringType, final Method actionMethod, final SpecificationLoader specificationLoader) { final Class<?>[] parameterTypes = actionMethod.getParameterTypes(); final Type[] genericParameterTypes = actionMethod.getGenericParameterTypes(); final List<FacetedMethodParameter> actionParams = Lists.newArrayList(); for (int paramNum = 0; paramNum < parameterTypes.length; paramNum++) { final Class<?> parameterType = parameterTypes[paramNum]; final Type genericParameterType = genericParameterTypes[paramNum]; final FeatureType featureType = org.apache.isis.core.metamodel.specloader.CollectionUtils .isParamCollection(parameterType, genericParameterType) ? FeatureType.ACTION_PARAMETER_COLLECTION : FeatureType.ACTION_PARAMETER_SCALAR; final FacetedMethodParameter fmp = new FacetedMethodParameter(featureType, declaringType, actionMethod, parameterType); actionParams.add(fmp); // this is based on similar logic to ActionAnnotationFacetFactory#processTypeOf if(featureType == FeatureType.ACTION_PARAMETER_COLLECTION) { final CollectionSemanticsFacet semanticsFacet = CollectionSemanticsFacetDefault.forParamType(parameterType, fmp); FacetUtil.addFacet(semanticsFacet); TypeOfFacet typeOfFacet = TypeOfFacet.Util .inferFromGenericParamType(fmp, parameterType, genericParameterType, specificationLoader); if(typeOfFacet == null ) { if (org.apache.isis.core.metamodel.specloader.CollectionUtils.isArrayType(parameterType)) { typeOfFacet = TypeOfFacet.Util.inferFromArrayType(fmp, parameterType, specificationLoader); } } // copy over (corresponds to similar code for OneToManyAssociation in FacetMethodsBuilder). if(typeOfFacet != null ) { FacetUtil.addFacet(typeOfFacet); fmp.setType(typeOfFacet.value()); } } } return Collections.unmodifiableList(actionParams); } // ////////////////////////////////////////////////// // Constructor // ////////////////////////////////////////////////// private final Class<?> owningType; private final Method method; private final Identifier identifier; private final List<FacetedMethodParameter> parameters; public List<FacetedMethodParameter> getParameters() { return parameters; } public static List<FacetedMethodParameter> emptyParameterList() { final List<FacetedMethodParameter> emptyList = Collections.emptyList(); return Collections.unmodifiableList(emptyList); } private FacetedMethod(final FeatureType featureType, final Class<?> declaringType, final Method method, final Class<?> type, final List<FacetedMethodParameter> parameters) { super(featureType, type); this.owningType = declaringType; this.method = method; this.identifier = featureType.identifierFor(declaringType, method); this.parameters = parameters; } /** * The {@link Class} that owns this {@link Method} (as per * {@link Class#getMethods()}, returning the {@link Method}s both of this * class and of its superclasses). * * <p> * Note: we don't call this the 'declaring type' because * {@link Class#getDeclaredMethods()} does not return methods from * superclasses. */ public Class<?> getOwningType() { return owningType; } /** * A {@link Method} obtained from the {@link #getOwningType() owning type} * using {@link Class#getMethods()}. */ public Method getMethod() { return method; } @Override public Identifier getIdentifier() { return identifier; } // //////////////////////////////////////////////////////////////////// // toString // //////////////////////////////////////////////////////////////////// @Override public String toString() { return getFeatureType().name() + " Peer [identifier=\"" + getIdentifier() + "\",type=" + getType().getName() + " ]"; } }