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
020 package org.apache.isis.core.progmodel.facets.collections.modify;
021
022 import java.lang.reflect.Method;
023
024 import org.apache.isis.core.commons.lang.NameUtils;
025 import org.apache.isis.core.metamodel.adapter.ObjectDirtier;
026 import org.apache.isis.core.metamodel.adapter.ObjectDirtierAware;
027 import org.apache.isis.core.metamodel.exceptions.MetaModelException;
028 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
029 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
030 import org.apache.isis.core.metamodel.facetapi.FeatureType;
031 import org.apache.isis.core.metamodel.facets.FacetFactory;
032 import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacet;
033 import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
034 import org.apache.isis.core.metamodel.methodutils.MethodScope;
035 import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
036 import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
037 import org.apache.isis.core.progmodel.facets.MethodPrefixConstants;
038 import org.apache.isis.core.progmodel.facets.collections.validate.CollectionValidateAddToFacetViaMethod;
039 import org.apache.isis.core.progmodel.facets.collections.validate.CollectionValidateRemoveFromFacetViaMethod;
040
041 /**
042 * TODO: should probably split out into two {@link FacetFactory}s, one for <tt>addTo()</tt>/<tt>removeFrom()</tt> and
043 * one for <tt>validateAddTo()</tt>/<tt>validateRemoveFrom()</tt>.
044 */
045 public class CollectionAddRemoveAndValidateFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements
046 ObjectDirtierAware {
047
048 private static final String[] PREFIXES = {};
049
050 private ObjectDirtier objectDirtier;
051
052 public CollectionAddRemoveAndValidateFacetFactory() {
053 super(FeatureType.COLLECTIONS_ONLY, PREFIXES);
054 }
055
056 @Override
057 public void process(final ProcessMethodContext processMethodContext) {
058
059 final Class<?> collectionType = attachAddToFacetAndRemoveFromFacet(processMethodContext);
060 attachValidateAddToAndRemoveFromFacetIfMethodsFound(processMethodContext, collectionType);
061 }
062
063 private Class<?> attachAddToFacetAndRemoveFromFacet(final ProcessMethodContext processMethodContext) {
064
065 final Method accessorMethod = processMethodContext.getMethod();
066 final String capitalizedName = NameUtils.javaBaseName(accessorMethod.getName());
067
068 final Class<?> cls = processMethodContext.getCls();
069
070 // add
071 final Method addToMethod =
072 MethodFinderUtils.findMethod(cls, MethodScope.OBJECT,
073 MethodPrefixConstants.ADD_TO_PREFIX + capitalizedName, void.class);
074 processMethodContext.removeMethod(addToMethod);
075
076 // remove
077 final Method removeFromMethod =
078 MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.REMOVE_FROM_PREFIX
079 + capitalizedName, void.class);
080 processMethodContext.removeMethod(removeFromMethod);
081
082 // add facets
083 final FacetHolder collection = processMethodContext.getFacetHolder();
084 FacetUtil.addFacet(createAddToFacet(addToMethod, accessorMethod, collection));
085 FacetUtil.addFacet(createRemoveFromFacet(removeFromMethod, accessorMethod, collection));
086
087 // infer typ
088 final Class<?> addToType =
089 ((addToMethod == null || addToMethod.getParameterTypes().length != 1) ? null : addToMethod
090 .getParameterTypes()[0]);
091 final Class<?> removeFromType =
092 ((removeFromMethod == null || removeFromMethod.getParameterTypes().length != 1) ? null : removeFromMethod
093 .getParameterTypes()[0]);
094
095 return inferTypeOfIfPossible(accessorMethod, addToType, removeFromType, collection);
096 }
097
098 /**
099 * TODO need to distinguish between Java collections, arrays and other collections!
100 */
101 private CollectionAddToFacet createAddToFacet(final Method addToMethodIfAny, final Method accessorMethod,
102 final FacetHolder holder) {
103 if (addToMethodIfAny != null) {
104 return new CollectionAddToFacetViaMethod(addToMethodIfAny, holder);
105 } else {
106 return new CollectionAddToFacetViaAccessor(accessorMethod, holder, getObjectDirtier());
107 }
108 }
109
110 /**
111 * TODO need to distinguish between Java collections, arrays and other collections!
112 */
113 private CollectionRemoveFromFacet createRemoveFromFacet(final Method removeFromMethodIfAny,
114 final Method accessorMethod, final FacetHolder holder) {
115 if (removeFromMethodIfAny != null) {
116 return new CollectionRemoveFromFacetViaMethod(removeFromMethodIfAny, holder);
117 } else {
118 return new CollectionRemoveFromFacetViaAccessor(accessorMethod, holder, getObjectDirtier());
119 }
120 }
121
122 private Class<?> inferTypeOfIfPossible(final Method getMethod, final Class<?> addType, final Class<?> removeType,
123 final FacetHolder collection) {
124
125 if (addType != null && removeType != null && addType != removeType) {
126 throw new MetaModelException("The addTo/removeFrom methods for " + getMethod.getDeclaringClass() + " must "
127 + "both deal with same type of object: " + addType + "; " + removeType);
128 }
129
130 final Class<?> type = addType != null ? addType : removeType;
131 if (type != null) {
132 FacetUtil
133 .addFacet(new TypeOfFacetInferredFromSupportingMethods(type, collection, getSpecificationLookup()));
134 }
135 return type;
136 }
137
138 private void attachValidateAddToAndRemoveFromFacetIfMethodsFound(final ProcessMethodContext processMethodContext,
139 final Class<?> collectionType) {
140 attachValidateAddToFacetIfValidateAddToMethodIsFound(processMethodContext, collectionType);
141 attachValidateRemoveFacetIfValidateRemoveFromMethodIsFound(processMethodContext, collectionType);
142 }
143
144 private void attachValidateAddToFacetIfValidateAddToMethodIsFound(final ProcessMethodContext processMethodContext,
145 final Class<?> collectionType) {
146
147 final Method getMethod = processMethodContext.getMethod();
148 final String capitalizedName = NameUtils.javaBaseName(getMethod.getName());
149
150 final Class<?> cls = processMethodContext.getCls();
151 final Class<?>[] paramTypes = MethodFinderUtils.paramTypesOrNull(collectionType);
152 Method validateAddToMethod =
153 MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_ADD_TO_PREFIX
154 + capitalizedName, String.class, paramTypes);
155 if (validateAddToMethod == null) {
156 validateAddToMethod =
157 MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_ADD_TO_PREFIX_2
158 + capitalizedName, String.class, MethodFinderUtils.paramTypesOrNull(collectionType));
159 }
160 if (validateAddToMethod == null) {
161 return;
162 }
163 processMethodContext.removeMethod(validateAddToMethod);
164
165 final FacetHolder collection = processMethodContext.getFacetHolder();
166 FacetUtil.addFacet(new CollectionValidateAddToFacetViaMethod(validateAddToMethod, collection));
167 }
168
169 private void attachValidateRemoveFacetIfValidateRemoveFromMethodIsFound(
170 final ProcessMethodContext processMethodContext, final Class<?> collectionType) {
171
172 final Method getMethod = processMethodContext.getMethod();
173 final String capitalizedName = NameUtils.javaBaseName(getMethod.getName());
174
175 final Class<?> cls = processMethodContext.getCls();
176 final Class<?>[] paramTypes = MethodFinderUtils.paramTypesOrNull(collectionType);
177 Method validateRemoveFromMethod =
178 MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_REMOVE_FROM_PREFIX
179 + capitalizedName, String.class, paramTypes);
180 if (validateRemoveFromMethod == null) {
181 validateRemoveFromMethod =
182 MethodFinderUtils.findMethod(cls, MethodScope.OBJECT,
183 MethodPrefixConstants.VALIDATE_REMOVE_FROM_PREFIX_2 + capitalizedName, String.class,
184 MethodFinderUtils.paramTypesOrNull(collectionType));
185 }
186 if (validateRemoveFromMethod == null) {
187 return;
188 }
189 processMethodContext.removeMethod(validateRemoveFromMethod);
190
191 final FacetHolder collection = processMethodContext.getFacetHolder();
192 FacetUtil.addFacet(new CollectionValidateRemoveFromFacetViaMethod(validateRemoveFromMethod, collection));
193 }
194
195 // ///////////////////////////////////////////////////////
196 // Dependencies (injected)
197 // ///////////////////////////////////////////////////////
198
199 protected ObjectDirtier getObjectDirtier() {
200 return objectDirtier;
201 }
202
203 @Override
204 public void setObjectDirtier(final ObjectDirtier objectDirtier) {
205 this.objectDirtier = objectDirtier;
206 }
207
208 }