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.specloader.specimpl; 021 022import java.util.List; 023 024import com.google.common.collect.Lists; 025 026import org.apache.isis.applib.annotation.Where; 027import org.apache.isis.applib.query.Query; 028import org.apache.isis.applib.query.QueryFindAllInstances; 029import org.apache.isis.core.commons.authentication.AuthenticationSession; 030import org.apache.isis.core.commons.debug.DebugString; 031import org.apache.isis.core.commons.exceptions.IsisException; 032import org.apache.isis.core.commons.util.ToString; 033import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 034import org.apache.isis.core.metamodel.consent.Consent; 035import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod; 036import org.apache.isis.core.metamodel.consent.InteractionResult; 037import org.apache.isis.core.metamodel.facetapi.FeatureType; 038import org.apache.isis.core.metamodel.facets.FacetedMethod; 039import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet; 040import org.apache.isis.core.metamodel.facets.mandatory.MandatoryFacet; 041import org.apache.isis.core.metamodel.facets.object.bounded.ChoicesFacetUtils; 042import org.apache.isis.core.metamodel.facets.properties.autocomplete.PropertyAutoCompleteFacet; 043import org.apache.isis.core.metamodel.facets.properties.choices.PropertyChoicesFacet; 044import org.apache.isis.core.metamodel.facets.properties.defaults.PropertyDefaultFacet; 045import org.apache.isis.core.metamodel.facets.properties.modify.PropertyClearFacet; 046import org.apache.isis.core.metamodel.facets.properties.modify.PropertyInitializationFacet; 047import org.apache.isis.core.metamodel.facets.properties.modify.PropertySetterFacet; 048import org.apache.isis.core.metamodel.interactions.InteractionUtils; 049import org.apache.isis.core.metamodel.interactions.PropertyAccessContext; 050import org.apache.isis.core.metamodel.interactions.PropertyModifyContext; 051import org.apache.isis.core.metamodel.interactions.PropertyUsabilityContext; 052import org.apache.isis.core.metamodel.interactions.PropertyVisibilityContext; 053import org.apache.isis.core.metamodel.interactions.UsabilityContext; 054import org.apache.isis.core.metamodel.interactions.ValidityContext; 055import org.apache.isis.core.metamodel.interactions.VisibilityContext; 056import org.apache.isis.core.metamodel.spec.Instance; 057import org.apache.isis.core.metamodel.spec.ObjectSpecification; 058import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext; 059import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; 060import org.apache.isis.core.progmodel.facets.param.autocomplete.MinLengthUtil; 061 062public class OneToOneAssociationImpl extends ObjectAssociationAbstract implements OneToOneAssociation { 063 064 public OneToOneAssociationImpl(final FacetedMethod facetedMethod, final ObjectMemberContext objectMemberContext) { 065 this(facetedMethod, getSpecification(objectMemberContext.getSpecificationLookup(), facetedMethod.getType()), objectMemberContext); 066 } 067 068 protected OneToOneAssociationImpl(final FacetedMethod facetedMethod, final ObjectSpecification objectSpec, final ObjectMemberContext objectMemberContext) { 069 super(facetedMethod, FeatureType.PROPERTY, objectSpec, objectMemberContext); 070 } 071 072 // ///////////////////////////////////////////////////////////// 073 // Hidden (or visible) 074 // ///////////////////////////////////////////////////////////// 075 076 @Override 077 public VisibilityContext<?> createVisibleInteractionContext(final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final ObjectAdapter ownerAdapter, Where where) { 078 return new PropertyVisibilityContext(getDeploymentCategory(), session, invocationMethod, ownerAdapter, getIdentifier(), where); 079 } 080 081 // ///////////////////////////////////////////////////////////// 082 // Disabled (or enabled) 083 // ///////////////////////////////////////////////////////////// 084 085 @Override 086 public UsabilityContext<?> createUsableInteractionContext(final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final ObjectAdapter ownerAdapter, Where where) { 087 return new PropertyUsabilityContext(getDeploymentCategory(), session, invocationMethod, ownerAdapter, getIdentifier(), where); 088 } 089 090 // ///////////////////////////////////////////////////////////// 091 // Validate 092 // ///////////////////////////////////////////////////////////// 093 094 @Override 095 public ValidityContext<?> createValidateInteractionContext(final AuthenticationSession session, final InteractionInvocationMethod interactionMethod, final ObjectAdapter ownerAdapter, final ObjectAdapter proposedToReferenceAdapter) { 096 return new PropertyModifyContext(getDeploymentCategory(), session, interactionMethod, ownerAdapter, getIdentifier(), proposedToReferenceAdapter); 097 } 098 099 /** 100 * TODO: currently this method is hard-coded to assume all interactions are 101 * initiated {@link InteractionInvocationMethod#BY_USER by user}. 102 */ 103 @Override 104 public Consent isAssociationValid(final ObjectAdapter ownerAdapter, final ObjectAdapter proposedToReferenceAdapter) { 105 return isAssociationValidResult(ownerAdapter, proposedToReferenceAdapter).createConsent(); 106 } 107 108 private InteractionResult isAssociationValidResult(final ObjectAdapter ownerAdapter, final ObjectAdapter proposedToReferenceAdapter) { 109 final ValidityContext<?> validityContext = createValidateInteractionContext(getAuthenticationSession(), InteractionInvocationMethod.BY_USER, ownerAdapter, proposedToReferenceAdapter); 110 return InteractionUtils.isValidResult(this, validityContext); 111 } 112 113 // ///////////////////////////////////////////////////////////// 114 // init 115 // ///////////////////////////////////////////////////////////// 116 117 @Override 118 public void initAssociation(final ObjectAdapter ownerAdapter, final ObjectAdapter referencedAdapter) { 119 final PropertyInitializationFacet initializerFacet = getFacet(PropertyInitializationFacet.class); 120 if (initializerFacet != null) { 121 initializerFacet.initProperty(ownerAdapter, referencedAdapter); 122 } 123 } 124 125 // ///////////////////////////////////////////////////////////// 126 // Access (get, isEmpty) 127 // ///////////////////////////////////////////////////////////// 128 129 @Override 130 public ObjectAdapter get(final ObjectAdapter ownerAdapter) { 131 final PropertyOrCollectionAccessorFacet facet = getFacet(PropertyOrCollectionAccessorFacet.class); 132 final Object referencedPojo = facet.getProperty(ownerAdapter); 133 134 if (referencedPojo == null) { 135 return null; 136 } 137 138 return getAdapterManager().adapterFor(referencedPojo, ownerAdapter); 139 } 140 141 /** 142 * TODO: currently this method is hard-coded to assume all interactions are 143 * initiated {@link InteractionInvocationMethod#BY_USER by user}. 144 */ 145 @Override 146 public PropertyAccessContext createAccessInteractionContext(final AuthenticationSession session, final InteractionInvocationMethod interactionMethod, final ObjectAdapter ownerAdapter) { 147 return new PropertyAccessContext(getDeploymentCategory(), session, InteractionInvocationMethod.BY_USER, ownerAdapter, getIdentifier(), get(ownerAdapter)); 148 } 149 150 @Override 151 public boolean isEmpty(final ObjectAdapter ownerAdapter) { 152 return get(ownerAdapter) == null; 153 } 154 155 // ///////////////////////////////////////////////////////////// 156 // Set 157 // ///////////////////////////////////////////////////////////// 158 159 @Override 160 public void set(final ObjectAdapter ownerAdapter, final ObjectAdapter newReferencedAdapter) { 161 if (newReferencedAdapter != null) { 162 setValue(ownerAdapter, newReferencedAdapter); 163 } else { 164 clearValue(ownerAdapter); 165 } 166 } 167 168 /** 169 * @see #set(ObjectAdapter, ObjectAdapter) 170 */ 171 @Deprecated 172 @Override 173 public void setAssociation(final ObjectAdapter ownerAdapter, final ObjectAdapter newReferencedAdapter) { 174 setValue(ownerAdapter, newReferencedAdapter); 175 } 176 177 private void setValue(final ObjectAdapter ownerAdapter, final ObjectAdapter newReferencedAdapter) { 178 final PropertySetterFacet setterFacet = getFacet(PropertySetterFacet.class); 179 if (setterFacet == null) { 180 return; 181 } 182 if (ownerAdapter.representsPersistent() && newReferencedAdapter != null && newReferencedAdapter.isTransient() && !newReferencedAdapter.getSpecification().isParented()) { 183 // TODO: move to facet ? 184 throw new IsisException("can't set a reference to a transient object from a persistent one: " + newReferencedAdapter.titleString() + " (transient)"); 185 } 186 setterFacet.setProperty(ownerAdapter, newReferencedAdapter); 187 } 188 189 /** 190 * @see #set(ObjectAdapter, ObjectAdapter) 191 */ 192 @Deprecated 193 @Override 194 public void clearAssociation(final ObjectAdapter ownerAdapter) { 195 clearValue(ownerAdapter); 196 } 197 198 private void clearValue(final ObjectAdapter ownerAdapter) { 199 final PropertyClearFacet facet = getFacet(PropertyClearFacet.class); 200 facet.clearProperty(ownerAdapter); 201 } 202 203 // ///////////////////////////////////////////////////////////// 204 // defaults 205 // ///////////////////////////////////////////////////////////// 206 207 @Override 208 public ObjectAdapter getDefault(final ObjectAdapter ownerAdapter) { 209 PropertyDefaultFacet propertyDefaultFacet = getFacet(PropertyDefaultFacet.class); 210 // if no default on the association, attempt to find a default on the 211 // specification (eg an int should 212 // default to 0). 213 if (propertyDefaultFacet == null || propertyDefaultFacet.isNoop()) { 214 propertyDefaultFacet = this.getSpecification().getFacet(PropertyDefaultFacet.class); 215 } 216 if (propertyDefaultFacet == null) { 217 return null; 218 } 219 return propertyDefaultFacet.getDefault(ownerAdapter); 220 } 221 222 @Override 223 public void toDefault(final ObjectAdapter ownerAdapter) { 224 // don't default optional fields 225 final MandatoryFacet mandatoryFacet = getFacet(MandatoryFacet.class); 226 if (mandatoryFacet != null && mandatoryFacet.isInvertedSemantics()) { 227 return; 228 } 229 230 final ObjectAdapter defaultValue = getDefault(ownerAdapter); 231 if (defaultValue != null) { 232 initAssociation(ownerAdapter, defaultValue); 233 } 234 } 235 236 // ///////////////////////////////////////////////////////////// 237 // choices and autoComplete 238 // ///////////////////////////////////////////////////////////// 239 240 @Override 241 public boolean hasChoices() { 242 return getFacet(PropertyChoicesFacet.class) != null; 243 } 244 245 @Override 246 public ObjectAdapter[] getChoices(final ObjectAdapter ownerAdapter) { 247 final PropertyChoicesFacet propertyChoicesFacet = getFacet(PropertyChoicesFacet.class); 248 final Object[] pojoOptions = propertyChoicesFacet == null ? null : propertyChoicesFacet.getChoices(ownerAdapter, getSpecificationLookup()); 249 if (pojoOptions != null) { 250 List<ObjectAdapter> adapters = Lists.transform( 251 Lists.newArrayList(pojoOptions), ObjectAdapter.Functions.adapterForUsing(getAdapterManager())); 252 return adapters.toArray(new ObjectAdapter[]{}); 253 } 254 // // now incorporated into above choices processing (BoundedFacet is no more) 255 /* else if (BoundedFacetUtils.isBoundedSet(getSpecification())) { 256 return options(); 257 } */ 258 return null; 259 } 260 261 private <T> ObjectAdapter[] options() { 262 final Query<T> query = new QueryFindAllInstances<T>(getSpecification().getFullIdentifier()); 263 final List<ObjectAdapter> allInstancesAdapter = getQuerySubmitter().allMatchingQuery(query); 264 final ObjectAdapter[] options = new ObjectAdapter[allInstancesAdapter.size()]; 265 int j = 0; 266 for (final ObjectAdapter adapter : allInstancesAdapter) { 267 options[j++] = adapter; 268 } 269 return options; 270 } 271 272 273 @Override 274 public boolean hasAutoComplete() { 275 final PropertyAutoCompleteFacet propertyAutoCompleteFacet = getFacet(PropertyAutoCompleteFacet.class); 276 return propertyAutoCompleteFacet != null; 277 } 278 279 @Override 280 public ObjectAdapter[] getAutoComplete(ObjectAdapter ownerAdapter, String searchArg) { 281 final PropertyAutoCompleteFacet propertyAutoCompleteFacet = getFacet(PropertyAutoCompleteFacet.class); 282 final Object[] pojoOptions = propertyAutoCompleteFacet.autoComplete(ownerAdapter, searchArg); 283 if (pojoOptions != null) { 284 final ObjectAdapter[] options = new ObjectAdapter[pojoOptions.length]; 285 for (int i = 0; i < options.length; i++) { 286 options[i] = getAdapterManager().adapterFor(pojoOptions[i]); 287 } 288 return options; 289 } 290 return null; 291 } 292 293 @Override 294 public int getAutoCompleteMinLength() { 295 final PropertyAutoCompleteFacet propertyAutoCompleteFacet = getFacet(PropertyAutoCompleteFacet.class); 296 return propertyAutoCompleteFacet != null? propertyAutoCompleteFacet.getMinLength(): MinLengthUtil.MIN_LENGTH_DEFAULT; 297 } 298 299 300 // ///////////////////////////////////////////////////////////// 301 // getInstance 302 // ///////////////////////////////////////////////////////////// 303 304 @Override 305 public Instance getInstance(final ObjectAdapter ownerAdapter) { 306 final OneToOneAssociation specification = this; 307 return ownerAdapter.getInstance(specification); 308 } 309 310 // ///////////////////////////////////////////////////////////// 311 // debug, toString 312 // ///////////////////////////////////////////////////////////// 313 314 @Override 315 public String debugData() { 316 final DebugString debugString = new DebugString(); 317 debugString.indent(); 318 debugString.indent(); 319 getFacetedMethod().debugData(debugString); 320 return debugString.toString(); 321 } 322 323 @Override 324 public String toString() { 325 final ToString str = new ToString(this); 326 str.append(super.toString()); 327 str.setAddComma(); 328 str.append("persisted", !isNotPersisted()); 329 str.append("type", getSpecification().getShortIdentifier()); 330 return str.toString(); 331 } 332 333 334}