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.List;
024import java.util.Properties;
025
026import com.google.common.collect.Lists;
027
028import org.apache.isis.applib.Identifier;
029import org.apache.isis.core.commons.lang.PropertiesExtensions;
030import org.apache.isis.core.metamodel.facetapi.Facet;
031import org.apache.isis.core.metamodel.facetapi.FacetHolder;
032import org.apache.isis.core.metamodel.facetapi.FeatureType;
033import org.apache.isis.core.metamodel.facetapi.MethodRemover;
034import org.apache.isis.core.metamodel.methodutils.MethodScope;
035import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
036import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
037import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
038import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
039
040public interface FacetFactory {
041
042    static class AbstractProcessContext<T extends FacetHolder> {
043        private final T facetHolder;
044
045        public AbstractProcessContext(final T facetHolder) {
046            this.facetHolder = facetHolder;
047        }
048
049        public T getFacetHolder() {
050            return facetHolder;
051        }
052    }
053    
054    public interface ProcessContextWithMetadataProperties<T extends FacetHolder> {
055        public Properties metadataProperties(String subKey);
056        public T getFacetHolder();
057    }
058
059    /**
060     * The {@link FeatureType feature type}s that this facet factory can create
061     * {@link Facet}s for.
062     * 
063     * <p>
064     * Used by the Java5 Reflector's <tt>ProgrammingModel</tt> to reduce the
065     * number of {@link FacetFactory factory}s that are queried when building up
066     * the meta-model.
067     */
068    List<FeatureType> getFeatureTypes();
069    
070    
071    // //////////////////////////////////////
072    // process class
073    // //////////////////////////////////////
074
075    public static class ProcessClassContext extends AbstractProcessContext<FacetHolder> implements MethodRemover, ProcessContextWithMetadataProperties<FacetHolder> {
076        private final Class<?> cls;
077        private final MethodRemover methodRemover;
078        private final Properties metadataProperties;
079
080        /**
081         * For testing only.
082         */
083        public ProcessClassContext(final Class<?> cls, final MethodRemover methodRemover, final FacetHolder facetHolder) {
084            this(cls, null, methodRemover, facetHolder);
085        }
086
087        public ProcessClassContext(
088                final Class<?> cls, 
089                final Properties metadataProperties, 
090                final MethodRemover methodRemover, 
091                final FacetHolder facetHolder) {
092            super(facetHolder);
093            this.cls = cls;
094            this.methodRemover = methodRemover;
095            this.metadataProperties = metadataProperties;
096        }
097
098        /**
099         * The class being processed.
100         */
101        public Class<?> getCls() {
102            return cls;
103        }
104
105        @Override
106        public void removeMethod(final Method method) {
107            methodRemover.removeMethod(method);
108        }
109
110        @Override
111        public List<Method> removeMethods(final MethodScope methodScope, final String prefix, final Class<?> returnType, final boolean canBeVoid, final int paramCount) {
112            return methodRemover.removeMethods(methodScope, prefix, returnType, canBeVoid, paramCount);
113        }
114
115        @Override
116        public void removeMethod(final MethodScope methodScope, final String methodName, final Class<?> returnType, final Class<?>[] parameterTypes) {
117            methodRemover.removeMethod(methodScope, methodName, returnType, parameterTypes);
118        }
119
120        @Override
121        public void removeMethods(final List<Method> methods) {
122            methodRemover.removeMethods(methods);
123        }
124
125        public Properties metadataProperties(String subKey) {
126            if(metadataProperties == null) {
127                return null;
128            }
129            final Properties subsetProperties = PropertiesExtensions.subset(this.metadataProperties, "class." + subKey);
130            return !subsetProperties.isEmpty() ? subsetProperties : null;
131        }
132    }
133
134    /**
135     * Process the class, and return the correctly setup annotation if present.
136     */
137    void process(ProcessClassContext processClassContext);
138    
139    // //////////////////////////////////////
140    // process method
141    // //////////////////////////////////////
142
143
144    public static class ProcessMethodContext extends AbstractProcessContext<FacetedMethod> implements MethodRemover, ProcessContextWithMetadataProperties<FacetedMethod> {
145        private final Class<?> cls;
146        private final FeatureType featureType;
147        private final Properties metadataProperties;
148        private final Method method;
149        private final MethodRemover methodRemover;
150
151        public ProcessMethodContext(
152                final Class<?> cls, 
153                final FeatureType featureType, 
154                final Properties metadataProperties, 
155                final Method method, 
156                final MethodRemover methodRemover, 
157                final FacetedMethod facetedMethod) {
158            super(facetedMethod);
159            this.cls = cls;
160            this.featureType = featureType;
161            this.metadataProperties = metadataProperties;
162            this.method = method;
163            this.methodRemover = methodRemover;
164        }
165
166        public Class<?> getCls() {
167            return cls;
168        }
169
170        public Method getMethod() {
171            return method;
172        }
173
174        @Override
175        public List<Method> removeMethods(final MethodScope methodScope, final String prefix, final Class<?> returnType, final boolean canBeVoid, final int paramCount) {
176            return methodRemover.removeMethods(methodScope, prefix, returnType, canBeVoid, paramCount);
177        }
178
179        @Override
180        public void removeMethod(final MethodScope methodScope, final String methodName, final Class<?> returnType, final Class<?>[] parameterTypes) {
181            methodRemover.removeMethod(methodScope, methodName, returnType, parameterTypes);
182        }
183
184        @Override
185        public void removeMethod(final Method method) {
186            methodRemover.removeMethod(method);
187        }
188
189        @Override
190        public void removeMethods(final List<Method> methods) {
191            methodRemover.removeMethods(methods);
192        }
193
194        public Properties metadataProperties(String subKey) {
195            
196            if(metadataProperties == null) {
197                return null;
198            }
199            Identifier identifier = featureType.identifierFor(getCls(), getMethod());
200            final String id = identifier.getMemberName();
201            
202            // build list of keys to search for... 
203            final List<String> keys = Lists.newArrayList();
204            if(featureType == FeatureType.ACTION) {
205                // ... either "action.actionId" or "member.actionId()" 
206                keys.add("action." + id+"."+subKey);
207                keys.add("member." + id+"()"+"."+subKey);
208            } else if(featureType == FeatureType.PROPERTY) {
209                // ... either "property.propertyId" or "member.propertyId" 
210                keys.add("property." + id+"."+subKey);
211                keys.add("member." + id+"."+subKey);
212            } else if(featureType == FeatureType.COLLECTION) {
213                // ... either "collection.collectionId" or "member.collectionId" 
214                keys.add("collection." + id+"."+subKey);
215                keys.add("member." + id+"."+subKey);
216            }
217
218            for (final String key : keys) {
219                final Properties subsetProperties = PropertiesExtensions.subset(this.metadataProperties, key);
220                if (!subsetProperties.isEmpty()) {
221                    return subsetProperties;
222                } 
223            }
224            
225            return null;
226        }
227    }
228
229    /**
230     * Process the method, and return the correctly setup annotation if present.
231     */
232    void process(ProcessMethodContext processMethodContext);
233
234
235
236
237    // //////////////////////////////////////
238    // process param
239    // //////////////////////////////////////
240
241    public static class ProcessParameterContext extends AbstractProcessContext<FacetedMethodParameter> {
242        private final Method method;
243        private final int paramNum;
244
245        public ProcessParameterContext(
246                final Method method, 
247                final int paramNum, 
248                final FacetedMethodParameter facetedMethodParameter) {
249            super(facetedMethodParameter);
250            this.method = method;
251            this.paramNum = paramNum;
252        }
253
254        public Method getMethod() {
255            return method;
256        }
257
258        public int getParamNum() {
259            return paramNum;
260        }
261    }
262
263    /**
264     * Process the parameters of the method, and return the correctly setup
265     * annotation if present.
266     */
267    void processParams(ProcessParameterContext processParameterContext);
268}