001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.metamodel.facets;
021
022import java.lang.reflect.Method;
023import java.util.Collections;
024import java.util.List;
025
026import com.google.common.collect.Lists;
027
028import org.apache.isis.applib.Identifier;
029import org.apache.isis.core.commons.debug.DebugBuilder;
030import org.apache.isis.core.commons.debug.Debuggable;
031import org.apache.isis.core.commons.lang.StringExtensions;
032import org.apache.isis.core.metamodel.facetapi.FeatureType;
033import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
034
035/**
036 * non-final only so it can be mocked if need be.
037 */
038public class FacetedMethod extends TypedHolderDefault implements IdentifiedHolder, Debuggable {
039
040    // //////////////////////////////////////////////////
041    // Factory methods
042    // //////////////////////////////////////////////////
043
044    /**
045     * Principally for testing purposes.
046     */
047    public static FacetedMethod createForProperty(final Class<?> declaringType, final String propertyName) {
048        try {
049            final Method method = declaringType.getMethod("get" + StringExtensions.asPascal(propertyName));
050            return FacetedMethod.createForProperty(declaringType, method);
051        } catch (final SecurityException e) {
052            throw new RuntimeException(e);
053        } catch (final NoSuchMethodException e) {
054            throw new RuntimeException(e);
055        }
056    }
057
058    /**
059     * Principally for testing purposes.
060     */
061    public static FacetedMethod createForCollection(final Class<?> declaringType, final String collectionName) {
062        try {
063            final Method method = declaringType.getMethod("get" + StringExtensions.asPascal(collectionName));
064            return FacetedMethod.createForCollection(declaringType, method);
065        } catch (final SecurityException e) {
066            throw new RuntimeException(e);
067        } catch (final NoSuchMethodException e) {
068            throw new RuntimeException(e);
069        }
070    }
071
072
073    public static FacetedMethod createForProperty(final Class<?> declaringType, final Method method) {
074        return new FacetedMethod(FeatureType.PROPERTY, declaringType, method, method.getReturnType(), emptyParameterList());
075    }
076
077    public static FacetedMethod createForCollection(final Class<?> declaringType, final Method method) {
078        return new FacetedMethod(FeatureType.COLLECTION, declaringType, method, null, emptyParameterList());
079    }
080
081    public static FacetedMethod createForAction(final Class<?> declaringType, final Method method) {
082        return new FacetedMethod(FeatureType.ACTION, declaringType, method, method.getReturnType(), getParameters(method));
083    }
084
085    private static List<FacetedMethodParameter> getParameters(final Method actionMethod) {
086        final Class<?>[] parameterTypes = actionMethod.getParameterTypes();
087        final List<FacetedMethodParameter> actionParams = Lists.newArrayList();
088        for (final Class<?> parameterType : parameterTypes) {
089            actionParams.add(new FacetedMethodParameter(parameterType));
090        }
091        return Collections.unmodifiableList(actionParams);
092    }
093
094    // //////////////////////////////////////////////////
095    // Constructor
096    // //////////////////////////////////////////////////
097
098    private final Class<?> owningType;
099    private final Method method;
100    private final Identifier identifier;
101    private final List<FacetedMethodParameter> parameters;
102
103    public List<FacetedMethodParameter> getParameters() {
104        return parameters;
105    }
106
107    public static List<FacetedMethodParameter> emptyParameterList() {
108        final List<FacetedMethodParameter> emptyList = Collections.emptyList();
109        return Collections.unmodifiableList(emptyList);
110    }
111
112    private FacetedMethod(final FeatureType featureType, final Class<?> declaringType, final Method method, final Class<?> type, final List<FacetedMethodParameter> parameters) {
113        super(featureType, type);
114        this.owningType = declaringType;
115        this.method = method;
116        this.identifier = featureType.identifierFor(declaringType, method);
117        this.parameters = parameters;
118    }
119
120    /**
121     * The {@link Class} that owns this {@link Method} (as per
122     * {@link Class#getMethods()}, returning the {@link Method}s both of this
123     * class and of its superclasses).
124     * 
125     * <p>
126     * Note: we don't call this the 'declaring type' because
127     * {@link Class#getDeclaredMethods()} does not return methods from
128     * superclasses.
129     */
130    public Class<?> getOwningType() {
131        return owningType;
132    }
133
134    /**
135     * A {@link Method} obtained from the {@link #getOwningType() owning type}
136     * using {@link Class#getMethods()}.
137     */
138    public Method getMethod() {
139        return method;
140    }
141
142    @Override
143    public Identifier getIdentifier() {
144        return identifier;
145    }
146
147    // ////////////////////////////////////////////////////////////////////
148    // toString, debug
149    // ////////////////////////////////////////////////////////////////////
150
151    @Override
152    public String toString() {
153        return getFeatureType().name() + " Peer [identifier=\"" + getIdentifier() + "\",type=" + getType().getName() + " ]";
154    }
155
156    @Override
157    public void debugData(final DebugBuilder debug) {
158        // TODO: reinstate
159        // debug.appendln("Identifier", identifier.toString());
160    }
161
162}