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.spec.feature; 021 022import java.util.List; 023import java.util.Map; 024 025import com.google.common.base.Function; 026import com.google.common.base.Predicate; 027import com.google.common.base.Strings; 028import com.google.common.collect.Lists; 029import com.google.common.collect.Maps; 030 031import org.apache.isis.applib.annotation.When; 032import org.apache.isis.applib.annotation.Where; 033import org.apache.isis.applib.filter.Filter; 034import org.apache.isis.core.commons.authentication.AuthenticationSession; 035import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 036import org.apache.isis.core.metamodel.consent.Consent; 037import org.apache.isis.core.metamodel.facets.hide.HiddenFacet; 038import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet; 039import org.apache.isis.core.metamodel.facets.object.value.ValueFacet; 040import org.apache.isis.core.metamodel.specloader.specimpl.ContributeeMember; 041 042/** 043 * Provides reflective access to a field on a domain object. 044 */ 045public interface ObjectAssociation extends ObjectMember, CurrentHolder { 046 047 /** 048 * Get the name for the business key, if one has been specified. 049 */ 050 String getBusinessKeyName(); 051 052 /** 053 * Return the default for this property. 054 */ 055 ObjectAdapter getDefault(ObjectAdapter adapter); 056 057 /** 058 * Set the property to it default references/values. 059 */ 060 public void toDefault(ObjectAdapter target); 061 062 /** 063 * Whether there are any choices provided (eg <tt>choicesXxx</tt> supporting 064 * method) for the association. 065 */ 066 public boolean hasChoices(); 067 /** 068 * Returns a list of possible references/values for this field, which the 069 * user can choose from. 070 */ 071 public ObjectAdapter[] getChoices(ObjectAdapter object); 072 073 074 /** 075 * Whether there are any auto-complete provided (eg <tt>autoCompleteXxx</tt> supporting 076 * method) for the association. 077 */ 078 public boolean hasAutoComplete(); 079 /** 080 * Returns a list of possible references/values for this field, which the 081 * user can choose from, based on the provided search argument. 082 */ 083 public ObjectAdapter[] getAutoComplete(ObjectAdapter object, String searchArg); 084 085 int getAutoCompleteMinLength(); 086 087 /** 088 * Returns true if calculated from other data in the object, that is, should 089 * not be persisted. 090 */ 091 boolean isNotPersisted(); 092 093 /** 094 * Returns <code>true</code> if this field on the specified object is deemed 095 * to be empty, or has no content. 096 */ 097 boolean isEmpty(ObjectAdapter target); 098 099 /** 100 * Determines if this field must be complete before the object is in a valid 101 * state 102 */ 103 boolean isMandatory(); 104 105 106 // ////////////////////////////////////////////////////// 107 // Functions 108 // ////////////////////////////////////////////////////// 109 110 public static class Functions { 111 private Functions(){} 112 113 public static Function<ObjectAssociation, String> toName() { 114 return new Function<ObjectAssociation, String>() { 115 @Override 116 public String apply(final ObjectAssociation oa) { 117 return oa.getName(); 118 } 119 }; 120 } 121 122 public static Function<ObjectAssociation, String> toId() { 123 return new Function<ObjectAssociation, String>() { 124 @Override 125 public String apply(final ObjectAssociation oa) { 126 return oa.getId(); 127 } 128 }; 129 } 130 } 131 132 // ////////////////////////////////////////////////////// 133 // Predicates 134 // ////////////////////////////////////////////////////// 135 136 public static class Predicates { 137 private Predicates(){} 138 139 public static Predicate<ObjectAssociation> being(final Contributed contributed) { 140 return new Predicate<ObjectAssociation>(){ 141 @Override 142 public boolean apply(final ObjectAssociation t) { 143 return contributed.isIncluded() || 144 !(t instanceof ContributeeMember); 145 } 146 }; 147 } 148 149 /** 150 * Only fields that are for properties (ie 1:1 associations) 151 */ 152 public final static Predicate<ObjectAssociation> PROPERTIES = 153 org.apache.isis.applib.filter.Filters.asPredicate(Filters.PROPERTIES); 154 155 /** 156 * Only fields that are for reference properties (ie 1:1 associations) 157 */ 158 public final static Predicate<ObjectAssociation> REFERENCE_PROPERTIES = 159 org.apache.isis.applib.filter.Filters.asPredicate(Filters.REFERENCE_PROPERTIES); 160 161 /** 162 * Only fields that are for properties (ie 1:1 associations) 163 */ 164 public final static Predicate<ObjectAssociation> WHERE_VISIBLE_IN_COLLECTION_TABLE = 165 org.apache.isis.applib.filter.Filters.asPredicate(Filters.WHERE_VISIBLE_IN_COLLECTION_TABLE); 166 167 /** 168 * Only fields that are for properties (ie 1:1 associations) 169 */ 170 public final static Predicate<ObjectAssociation> WHERE_VISIBLE_IN_STANDALONE_TABLE = 171 org.apache.isis.applib.filter.Filters.asPredicate(Filters.WHERE_VISIBLE_IN_STANDALONE_TABLE); 172 173 /** 174 * All fields (that is, excludes out nothing). 175 */ 176 public final static Predicate<ObjectAssociation> ALL = 177 org.apache.isis.applib.filter.Filters.asPredicate(Filters.ALL); 178 179 /** 180 * Only fields that are for collections (ie 1:m associations) 181 */ 182 public final static Predicate<ObjectAssociation> COLLECTIONS = 183 org.apache.isis.applib.filter.Filters.asPredicate(Filters.COLLECTIONS); 184 185 /** 186 * Only properties that are visible statically, ie have not been 187 * unconditionally hidden at compile time. 188 */ 189 public static final Predicate<ObjectAssociation> VISIBLE_AT_LEAST_SOMETIMES = 190 org.apache.isis.applib.filter.Filters.asPredicate(Filters.VISIBLE_AT_LEAST_SOMETIMES); 191 192 public static final Predicate<ObjectAssociation> staticallyVisible(final Where context) { 193 return org.apache.isis.applib.filter.Filters.asPredicate(Filters.staticallyVisible(context)); 194 } 195 196 public static final Predicate<ObjectAssociation> dynamicallyVisible(final AuthenticationSession session, final ObjectAdapter target, final Where where) { 197 return org.apache.isis.applib.filter.Filters.asPredicate(Filters.dynamicallyVisible(session, target, where)); 198 } 199 200 public static final Predicate<ObjectAssociation> enabled(final AuthenticationSession session, final ObjectAdapter adapter, final Where where) { 201 return org.apache.isis.applib.filter.Filters.asPredicate(Filters.enabled(session, adapter, where)); 202 } 203 204 } 205 206 // ////////////////////////////////////////////////////// 207 // Filters 208 // ////////////////////////////////////////////////////// 209 210 public static class Filters { 211 212 private Filters() { 213 } 214 215 /** 216 * Filters only fields that are for properties (ie 1:1 associations) 217 * 218 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 219 */ 220 @Deprecated 221 public final static Filter<ObjectAssociation> PROPERTIES = new Filter<ObjectAssociation>() { 222 @Override 223 public boolean accept(final ObjectAssociation association) { 224 return association.isOneToOneAssociation(); 225 } 226 }; 227 228 /** 229 * Filters only fields that are for reference properties (ie 1:1 associations) 230 * 231 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 232 */ 233 @Deprecated 234 public final static Filter<ObjectAssociation> REFERENCE_PROPERTIES = new Filter<ObjectAssociation>() { 235 @Override 236 public boolean accept(final ObjectAssociation association) { 237 return association.isOneToOneAssociation() && 238 !association.getSpecification().containsDoOpFacet(ValueFacet.class); 239 } 240 }; 241 242 /** 243 * Filters only fields that are for properties (ie 1:1 associations) 244 * 245 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 246 */ 247 @Deprecated 248 public final static Filter<ObjectAssociation> WHERE_VISIBLE_IN_COLLECTION_TABLE = new Filter<ObjectAssociation>() { 249 @Override 250 public boolean accept(final ObjectAssociation association) { 251 final HiddenFacet hiddenFacet = association.getFacet(HiddenFacet.class); 252 return hiddenFacet == null || !hiddenFacet.where().inParentedTable(); 253 } 254 }; 255 256 /** 257 * Filters only fields that are for properties (ie 1:1 associations) 258 * 259 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 260 */ 261 @Deprecated 262 public final static Filter<ObjectAssociation> WHERE_VISIBLE_IN_STANDALONE_TABLE = new Filter<ObjectAssociation>() { 263 @Override 264 public boolean accept(final ObjectAssociation association) { 265 final HiddenFacet hiddenFacet = association.getFacet(HiddenFacet.class); 266 return hiddenFacet == null || !hiddenFacet.where().inStandaloneTable(); 267 } 268 }; 269 270 /** 271 * Returns all fields (that is, filters out nothing). 272 * 273 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 274 */ 275 @Deprecated 276 public final static Filter<ObjectAssociation> ALL = new Filter<ObjectAssociation>() { 277 @Override 278 public boolean accept(final ObjectAssociation property) { 279 return true; 280 } 281 }; 282 283 /** 284 * Filters only fields that are for collections (ie 1:m associations) 285 * 286 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 287 */ 288 @Deprecated 289 public final static Filter<ObjectAssociation> COLLECTIONS = new Filter<ObjectAssociation>() { 290 @Override 291 public boolean accept(final ObjectAssociation property) { 292 return property.isOneToManyAssociation(); 293 } 294 }; 295 296 /** 297 * Filters only properties that are visible statically, ie have not been 298 * unconditionally hidden at compile time. 299 * 300 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 301 */ 302 @Deprecated 303 public static final Filter<ObjectAssociation> VISIBLE_AT_LEAST_SOMETIMES = new Filter<ObjectAssociation>() { 304 @Override 305 public boolean accept(final ObjectAssociation property) { 306 final HiddenFacet hiddenFacet = property.getFacet(HiddenFacet.class); 307 return hiddenFacet == null || hiddenFacet.when() != When.ALWAYS || hiddenFacet.where() != Where.ANYWHERE; 308 } 309 }; 310 311 /** 312 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 313 */ 314 @Deprecated 315 public static final Filter<ObjectAssociation> staticallyVisible(final Where context) { 316 return new Filter<ObjectAssociation>() { 317 @Override 318 public boolean accept(final ObjectAssociation association) { 319 final HiddenFacet facet = association.getFacet(HiddenFacet.class); 320 if(facet == null) { 321 return true; 322 } 323 return !(facet.where().includes(context) && facet.when() == When.ALWAYS); 324 } 325 }; 326 } 327 328 /** 329 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 330 */ 331 @Deprecated 332 public static Filter<ObjectAssociation> dynamicallyVisible(final AuthenticationSession session, final ObjectAdapter target, final Where where) { 333 return new Filter<ObjectAssociation>() { 334 @Override 335 public boolean accept(final ObjectAssociation objectAssociation) { 336 final Consent visible = objectAssociation.isVisible(session, target, where); 337 return visible.isAllowed(); 338 } 339 }; 340 } 341 342 /** 343 * @deprecated -use {@link com.google.common.base.Predicate equivalent} 344 */ 345 @Deprecated 346 public static Filter<ObjectAssociation> enabled(final AuthenticationSession session, final ObjectAdapter adapter, final Where where) { 347 return new Filter<ObjectAssociation>() { 348 @Override 349 public boolean accept(final ObjectAssociation objectAssociation) { 350 final Consent usable = objectAssociation.isUsable(session, adapter, where); 351 return usable.isAllowed(); 352 } 353 }; 354 } 355 356 } 357 358 // ////////////////////////////////////////////////////// 359 // Util 360 // ////////////////////////////////////////////////////// 361 362 public static class Util { 363 private Util(){} 364 365 public static Map<String, List<ObjectAssociation>> groupByMemberOrderName(List<ObjectAssociation> associations) { 366 Map<String, List<ObjectAssociation>> associationsByGroup = Maps.newHashMap(); 367 for(ObjectAssociation association: associations) { 368 addAssociationIntoGroup(associationsByGroup, association); 369 } 370 return associationsByGroup; 371 } 372 373 private static void addAssociationIntoGroup(Map<String, List<ObjectAssociation>> associationsByGroup, ObjectAssociation association) { 374 final MemberOrderFacet memberOrderFacet = association.getFacet(MemberOrderFacet.class); 375 if(memberOrderFacet != null) { 376 final String name = memberOrderFacet.name(); 377 if(!Strings.isNullOrEmpty(name)) { 378 getFrom(associationsByGroup, name).add(association); 379 return; 380 } 381 } 382 getFrom(associationsByGroup, "General").add(association); 383 } 384 385 private static List<ObjectAssociation> getFrom(Map<String, List<ObjectAssociation>> associationsByGroup, final String groupName) { 386 List<ObjectAssociation> list = associationsByGroup.get(groupName); 387 if(list == null) { 388 list = Lists.newArrayList(); 389 associationsByGroup.put(groupName, list); 390 } 391 return list; 392 } 393 } 394}