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; 024 025import com.google.common.collect.Lists; 026 027import org.apache.isis.applib.DomainObjectContainer; 028import org.apache.isis.applib.filter.Filter; 029import org.apache.isis.applib.filter.Filters; 030import org.apache.isis.applib.services.wrapper.WrapperFactory; 031import org.apache.isis.core.commons.lang.ObjectExtensions; 032import org.apache.isis.core.metamodel.facetapi.DecoratingFacet; 033import org.apache.isis.core.metamodel.facetapi.Facet; 034import org.apache.isis.core.metamodel.facetapi.FacetHolder; 035import org.apache.isis.core.metamodel.spec.ObjectSpecification; 036import org.apache.isis.core.metamodel.spec.feature.ObjectMember; 037 038/** 039 * A {@link Facet} implementation that ultimately wraps a {@link Method} or 040 * possibly several equivalent methods, for a Java implementation of a 041 * {@link ObjectMember}. 042 * 043 * <p> 044 * Used by <tt>ObjectSpecificationDefault#getMember(Method)</tt> in order to 045 * reverse lookup {@link ObjectMember}s from underlying {@link Method}s. So, for 046 * example, the facets that represents an action xxx, or an <tt>validateXxx</tt> 047 * method, or an <tt>addToXxx</tt> collection, can all be used to lookup the 048 * member. 049 * 050 * <p> 051 * Note that {@link Facet}s relating to the class itself (ie for 052 * {@link ObjectSpecification}) should not implement this interface. 053 */ 054public interface ImperativeFacet { 055 056 /** 057 * The {@link Method}s invoked by this {@link Facet}. 058 * 059 * <p> 060 * In the vast majority of cases there is only a single {@link Method} (eg 061 * wrapping a property's getter). However, some {@link Facet}s, such as 062 * those for callbacks, could map to multiple {@link Method}s. 063 * Implementations that will return multiple {@link Method}s should 064 * implement the {@link ImperativeFacetMulti} sub-interface that provides 065 * the ability to {@link ImperativeFacetMulti#addMethod(Method) add} 066 * {@link Method}s as part of the interface API. For example: 067 * 068 * <pre> 069 * if (someFacet instanceof ImperativeFacetMulti) { 070 * ImperativeFacetMulti ifm = (ImperativeFacetMulti)someFacet; 071 * ifm.addMethod(...); 072 * } 073 * </pre> 074 */ 075 public List<Method> getMethods(); 076 077 public static enum Intent { 078 CHECK_IF_HIDDEN, 079 CHECK_IF_DISABLED, 080 CHECK_IF_VALID, 081 ACCESSOR, 082 EXECUTE, 083 MODIFY_PROPERTY, 084 /** 085 * Modify property using modify/clear rather than simply using set. 086 */ 087 MODIFY_PROPERTY_SUPPORTING, 088 MODIFY_COLLECTION_ADD, 089 MODIFY_COLLECTION_REMOVE, 090 CHOICES_OR_AUTOCOMPLETE, 091 DEFAULTS, 092 INITIALIZATION, 093 LIFECYCLE, 094 UI_HINT 095 } 096 097 /** 098 * The intent of this method, so that the {@link WrapperFactory} knows whether to delegate on or to reject. 099 * @param method - one of the methods returned from {@link #getMethods()} 100 */ 101 public Intent getIntent(Method method); 102 103 104 /** 105 * For use by 106 * {@link FacetHolder#getFacets(org.apache.isis.core.metamodel.facetapi.progmodel.facets.org.apache.isis.nof.arch.facets.Facet.Filter)} 107 */ 108 public static Filter<Facet> FILTER = new Filter<Facet>() { 109 @Override 110 public boolean accept(final Facet facet) { 111 return ImperativeFacet.Util.isImperativeFacet(facet); 112 } 113 }; 114 115 /** 116 * Whether invoking this requires a 117 * {@link DomainObjectContainer#resolve(Object)} to occur first. 118 */ 119 public boolean impliesResolve(); 120 121 /** 122 * Whether invoking this method requires an 123 * {@link DomainObjectContainer#objectChanged(Object)} to occur afterwards. 124 * 125 * @return 126 */ 127 public boolean impliesObjectChanged(); 128 129 130 // ////////////////////////////////////// 131 132 public static class Util { 133 private Util(){} 134 135 /** 136 * Returns the provided {@link Facet facet} as an {@link ImperativeFacet} if 137 * it either is one or if it is a {@link DecoratingFacet} that in turn wraps 138 * an {@link ImperativeFacet}. 139 * 140 * <p> 141 * Otherwise, returns <tt>null</tt>. 142 */ 143 public static ImperativeFacet getImperativeFacet(final Facet facet) { 144 if (facet instanceof ImperativeFacet) { 145 return (ImperativeFacet) facet; 146 } 147 if (facet.getUnderlyingFacet() instanceof ImperativeFacet) { 148 return (ImperativeFacet) facet.getUnderlyingFacet(); 149 } 150 if (facet instanceof DecoratingFacet) { 151 final DecoratingFacet<?> decoratingFacet = ObjectExtensions.asT(facet); 152 return getImperativeFacet(decoratingFacet.getDecoratedFacet()); 153 } 154 return null; 155 } 156 157 public static boolean isImperativeFacet(final Facet facet) { 158 return getImperativeFacet(facet) != null; 159 } 160 161 public static Flags getFlags(final ObjectMember member, final Method method) { 162 final Flags flags = new Flags(); 163 if (member == null) { 164 return flags; 165 } 166 final List<Facet> allFacets = member.getFacets(Filters.anyOfType(Facet.class)); 167 for (final Facet facet : allFacets) { 168 final ImperativeFacet imperativeFacet = ImperativeFacet.Util.getImperativeFacet(facet); 169 if (imperativeFacet == null) { 170 continue; 171 } 172 final List<Method> methods = imperativeFacet.getMethods(); 173 if (!methods.contains(method)) { 174 continue; 175 } 176 flags.apply(imperativeFacet); 177 178 // no need to search further 179 if (flags.bothSet()) { 180 break; 181 } 182 } 183 return flags; 184 } 185 186 public static Intent getIntent(final ObjectMember member, final Method method) { 187 final List<Facet> allFacets = member.getFacets(Filters.anyOfType(Facet.class)); 188 final List<ImperativeFacet> imperativeFacets = Lists.newArrayList(); 189 for (final Facet facet : allFacets) { 190 final ImperativeFacet imperativeFacet = ImperativeFacet.Util.getImperativeFacet(facet); 191 if (imperativeFacet == null) { 192 continue; 193 } 194 final List<Method> methods = imperativeFacet.getMethods(); 195 if (!methods.contains(method)) { 196 continue; 197 } 198 imperativeFacets.add(imperativeFacet); 199 } 200 switch(imperativeFacets.size()) { 201 case 0: 202 break; 203 case 1: 204 return imperativeFacets.get(0).getIntent(method); 205 default: 206 Intent intentToReturn = null; 207 for (ImperativeFacet imperativeFacet : imperativeFacets) { 208 Intent intent = imperativeFacet.getIntent(method); 209 if(intentToReturn == null) { 210 intentToReturn = intent; 211 } else if(intentToReturn != intent) { 212 throw new IllegalArgumentException(member.getIdentifier().toClassAndNameIdentityString() + ": more than one ImperativeFacet for method " + method.getName() + " , with inconsistent intents: " + imperativeFacets.toString()); 213 } 214 } 215 return intentToReturn; 216 } 217 throw new IllegalArgumentException(member.getIdentifier().toClassAndNameIdentityString() + ": unable to determine intent of " + method.getName()); 218 } 219 } 220 221 public static class Flags { 222 private boolean impliesResolve; 223 private boolean impliesObjectChanged; 224 225 public void apply(final ImperativeFacet imperativeFacet) { 226 this.impliesResolve |= imperativeFacet.impliesResolve(); 227 this.impliesObjectChanged |= imperativeFacet.impliesObjectChanged(); 228 } 229 230 public boolean bothSet() { 231 return impliesResolve && impliesObjectChanged; 232 } 233 234 public boolean impliesResolve() { 235 return impliesResolve; 236 } 237 238 public boolean impliesObjectChanged() { 239 return impliesObjectChanged; 240 } 241 } 242 243}