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