/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.isis.viewer.wicket.model.models; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import com.google.common.collect.FluentIterable; import com.google.common.collect.Lists; import org.apache.isis.applib.annotation.PromptStyle; import org.apache.isis.applib.annotation.Where; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; import org.apache.isis.core.metamodel.consent.Consent; import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; import org.apache.isis.core.metamodel.deployment.DeploymentCategory; import org.apache.isis.core.metamodel.facetapi.Facet; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.facetapi.FeatureType; import org.apache.isis.core.metamodel.facets.object.parseable.ParseableFacet; import org.apache.isis.core.metamodel.facets.object.promptStyle.PromptStyleFacet; import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; import org.apache.isis.core.metamodel.facets.objectvalue.fileaccept.FileAcceptFacet; import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet; import org.apache.isis.core.metamodel.facets.objectvalue.typicallen.TypicalLengthFacet; import org.apache.isis.core.metamodel.facets.value.bigdecimal.BigDecimalValueFacet; import org.apache.isis.core.metamodel.facets.value.string.StringValueSemanticsProvider; import org.apache.isis.core.metamodel.spec.ObjectSpecId; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; import org.apache.isis.viewer.wicket.model.links.LinkAndLabel; import org.apache.isis.viewer.wicket.model.links.LinksProvider; import org.apache.isis.viewer.wicket.model.mementos.ActionParameterMemento; import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento; import org.apache.isis.viewer.wicket.model.mementos.PropertyMemento; import org.apache.isis.viewer.wicket.model.mementos.SpecUtils; /** * Represents a scalar of an entity, either a {@link Kind#PROPERTY property} or * a {@link Kind#PARAMETER parameter}. * * <p> * Is the backing model to each of the fields that appear in forms (for entities * or action dialogs). */ public class ScalarModel extends EntityModel implements LinksProvider,FormExecutorContext, ActionArgumentModel { private static final long serialVersionUID = 1L; public enum Kind { PROPERTY { @Override public String getName(final ScalarModel scalarModel) { return scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()).getName(); } @Override public ObjectSpecification getScalarTypeSpec(final ScalarModel scalarModel) { ObjectSpecId type = scalarModel.getPropertyMemento().getType(); return SpecUtils.getSpecificationFor(type, scalarModel.getSpecificationLoader()); } @Override public String getIdentifier(final ScalarModel scalarModel) { return scalarModel.getPropertyMemento().getIdentifier(); } @Override public String getLongName(final ScalarModel scalarModel) { ObjectSpecId objectSpecId = scalarModel.getParentEntityModel().getTypeOfSpecification().getSpecId(); final String specShortName = SpecUtils.getSpecificationFor(objectSpecId, scalarModel.getSpecificationLoader()).getShortIdentifier(); return specShortName + "-" + scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()).getId(); } @Override public String disable(final ScalarModel scalarModel, final Where where) { final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final OneToOneAssociation property = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()); try { final Consent usable = property.isUsable(parentAdapter, InteractionInitiatedBy.USER, where); return usable.isAllowed() ? null : usable.getReason(); } catch (final Exception ex) { return ex.getLocalizedMessage(); } } @Override public String parseAndValidate(final ScalarModel scalarModel, final String proposedPojoAsStr) { final OneToOneAssociation property = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()); ParseableFacet parseableFacet = property.getFacet(ParseableFacet.class); if (parseableFacet == null) { parseableFacet = property.getSpecification().getFacet(ParseableFacet.class); } try { final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final ObjectAdapter currentValue = property.get(parentAdapter, InteractionInitiatedBy.USER); final ObjectAdapter proposedAdapter = parseableFacet.parseTextEntry(currentValue, proposedPojoAsStr, InteractionInitiatedBy.USER); final Consent valid = property.isAssociationValid(parentAdapter, proposedAdapter, InteractionInitiatedBy.USER); return valid.isAllowed() ? null : valid.getReason(); } catch (final ConcurrencyException ex) { // disregard concurrency exceptions because will pick up at the IFormValidator level rather // than each individual property. return null; } catch (final Exception ex) { return ex.getLocalizedMessage(); } } @Override public String validate(final ScalarModel scalarModel, final ObjectAdapter proposedAdapter) { final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final OneToOneAssociation property = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()); try { final Consent valid = property.isAssociationValid(parentAdapter, proposedAdapter, InteractionInitiatedBy.USER); return valid.isAllowed() ? null : valid.getReason(); } catch (final Exception ex) { return ex.getLocalizedMessage(); } } @Override public boolean isRequired(final ScalarModel scalarModel) { final FacetHolder facetHolder = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()); return isRequired(facetHolder); } @Override public <T extends Facet> T getFacet(final ScalarModel scalarModel, final Class<T> facetType) { final FacetHolder facetHolder = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader()); return facetHolder.getFacet(facetType); } @Override public boolean hasChoices(final ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); return property.hasChoices(); } @Override public List<ObjectAdapter> getChoices( final ScalarModel scalarModel, final ObjectAdapter[] argumentsIfAvailable, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(ConcurrencyChecking.NO_CHECK); final ObjectAdapter[] choices = property.getChoices( parentAdapter, InteractionInitiatedBy.USER); return choicesAsList(choices); } @Override public boolean hasAutoComplete(final ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); return property.hasAutoComplete(); } @Override public List<ObjectAdapter> getAutoComplete( final ScalarModel scalarModel, final String searchArg, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(ConcurrencyChecking.NO_CHECK); final ObjectAdapter[] choices = property.getAutoComplete( parentAdapter, searchArg, InteractionInitiatedBy.USER); return choicesAsList(choices); } @Override public int getAutoCompleteOrChoicesMinLength(ScalarModel scalarModel) { if (scalarModel.hasAutoComplete()) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); return property.getAutoCompleteMinLength(); } else { return 0; } } @Override public void resetVersion(ScalarModel scalarModel) { scalarModel.getParentEntityModel().resetVersion(); } @Override public String getDescribedAs(final ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); return property.getDescription(); } @Override public Integer getLength(ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); final BigDecimalValueFacet facet = property.getFacet(BigDecimalValueFacet.class); return facet != null? facet.getLength(): null; } @Override public Integer getScale(ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); final BigDecimalValueFacet facet = property.getFacet(BigDecimalValueFacet.class); return facet != null? facet.getScale(): null; } @Override public int getTypicalLength(ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); final TypicalLengthFacet facet = property.getFacet(TypicalLengthFacet.class); return facet != null? facet.value() : StringValueSemanticsProvider.TYPICAL_LENGTH; } @Override public String getFileAccept(ScalarModel scalarModel) { final PropertyMemento propertyMemento = scalarModel.getPropertyMemento(); final OneToOneAssociation property = propertyMemento.getProperty(scalarModel.getSpecificationLoader()); final FileAcceptFacet facet = property.getFacet(FileAcceptFacet.class); return facet != null? facet.value(): null; } @Override public void init(final ScalarModel scalarModel) { reset(scalarModel); } @Override public void reset(ScalarModel scalarModel) { final OneToOneAssociation property = scalarModel.propertyMemento.getProperty(scalarModel.getSpecificationLoader()); final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final ObjectAdapter associatedAdapter = property.get(parentAdapter, InteractionInitiatedBy.USER); scalarModel.setObject(associatedAdapter); } @Override public ObjectAdapter load(final ScalarModel scalarModel) { return scalarModel.loadFromSuper(); } @Override public boolean isCollection(final ScalarModel scalarModel) { return false; } @Override public String toStringOf(final ScalarModel scalarModel) { return this.name() + ": " + scalarModel.getPropertyMemento().toString(); } }, PARAMETER { @Override public String getName(final ScalarModel scalarModel) { return scalarModel.getParameterMemento().getActionParameter(scalarModel.getSpecificationLoader()).getName(); } @Override public ObjectSpecification getScalarTypeSpec(final ScalarModel scalarModel) { return scalarModel.getParameterMemento().getSpecification(scalarModel.getSpecificationLoader()); } @Override public String getIdentifier(final ScalarModel scalarModel) { return "" + scalarModel.getParameterMemento().getNumber(); } @Override public String getLongName(final ScalarModel scalarModel) { final ObjectAdapterMemento adapterMemento = scalarModel.getObjectAdapterMemento(); if (adapterMemento == null) { // shouldn't happen return null; } ObjectSpecId objectSpecId = adapterMemento.getObjectSpecId(); final String specShortName = SpecUtils.getSpecificationFor(objectSpecId, scalarModel.getSpecificationLoader()).getShortIdentifier(); final String parmId = scalarModel.getParameterMemento().getActionParameter(scalarModel.getSpecificationLoader()).getIdentifier().toNameIdentityString(); return specShortName + "-" + parmId + "-" + scalarModel.getParameterMemento().getNumber(); } @Override public String disable(final ScalarModel scalarModel, Where where) { // always enabled return null; } @Override public String parseAndValidate(final ScalarModel scalarModel, final String proposedPojoAsStr) { final ObjectActionParameter parameter = scalarModel.getParameterMemento().getActionParameter( scalarModel.getSpecificationLoader()); try { final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final String invalidReasonIfAny = parameter.isValid(parentAdapter, proposedPojoAsStr, InteractionInitiatedBy.USER ); return invalidReasonIfAny; } catch (final Exception ex) { return ex.getLocalizedMessage(); } } @Override public String validate(final ScalarModel scalarModel, final ObjectAdapter proposedAdapter) { final ObjectActionParameter parameter = scalarModel.getParameterMemento().getActionParameter( scalarModel.getSpecificationLoader()); try { final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final String invalidReasonIfAny = parameter.isValid(parentAdapter, proposedAdapter.getObject(), InteractionInitiatedBy.USER ); return invalidReasonIfAny; } catch (final Exception ex) { return ex.getLocalizedMessage(); } } @Override public boolean isRequired(final ScalarModel scalarModel) { final FacetHolder facetHolder = scalarModel.getParameterMemento().getActionParameter( scalarModel.getSpecificationLoader()); return isRequired(facetHolder); } @Override public <T extends Facet> T getFacet(final ScalarModel scalarModel, final Class<T> facetType) { final FacetHolder facetHolder = scalarModel.getParameterMemento().getActionParameter( scalarModel.getSpecificationLoader()); return facetHolder.getFacet(facetType); } @Override public boolean hasChoices(final ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); return actionParameter.hasChoices(); } @Override public List<ObjectAdapter> getChoices( final ScalarModel scalarModel, final ObjectAdapter[] argumentsIfAvailable, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(); final ObjectAdapter[] choices = actionParameter.getChoices( parentAdapter, argumentsIfAvailable, InteractionInitiatedBy.USER); return choicesAsList(choices); } @Override public boolean hasAutoComplete(final ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); return actionParameter.hasAutoComplete(); } @Override public List<ObjectAdapter> getAutoComplete( final ScalarModel scalarModel, final String searchArg, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(ConcurrencyChecking.NO_CHECK); final ObjectAdapter[] choices = actionParameter.getAutoComplete( parentAdapter, searchArg, InteractionInitiatedBy.USER); return choicesAsList(choices); } @Override public int getAutoCompleteOrChoicesMinLength(ScalarModel scalarModel) { if (scalarModel.hasAutoComplete()) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter( scalarModel.getSpecificationLoader()); return actionParameter.getAutoCompleteMinLength(); } else { return 0; } } @Override public void resetVersion(ScalarModel scalarModel) { // no-op? } @Override public String getDescribedAs(final ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); return actionParameter.getDescription(); } @Override public Integer getLength(ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); final BigDecimalValueFacet facet = actionParameter.getFacet(BigDecimalValueFacet.class); return facet != null? facet.getLength(): null; } @Override public Integer getScale(ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); final BigDecimalValueFacet facet = actionParameter.getFacet(BigDecimalValueFacet.class); return facet != null? facet.getScale(): null; } @Override public int getTypicalLength(ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); final TypicalLengthFacet facet = actionParameter.getFacet(TypicalLengthFacet.class); return facet != null? facet.value() : StringValueSemanticsProvider.TYPICAL_LENGTH; } @Override public String getFileAccept(ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); final FileAcceptFacet facet = actionParameter.getFacet(FileAcceptFacet.class); return facet != null? facet.value(): null; } @Override public void init(final ScalarModel scalarModel) { // no-op } @Override public void reset(ScalarModel scalarModel) { final ObjectActionParameter actionParameter = scalarModel.parameterMemento.getActionParameter( scalarModel.getSpecificationLoader()); final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load(ConcurrencyChecking.NO_CHECK); final ObjectAdapter defaultAdapter = actionParameter.getDefault(parentAdapter); scalarModel.setObject(defaultAdapter); } @Override public ObjectAdapter load(final ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento .getActionParameter(scalarModel.getSpecificationLoader()); final ObjectAdapter objectAdapter = scalarModel.loadFromSuper(); if(objectAdapter != null) { return objectAdapter; } if(actionParameter.getFeatureType() == FeatureType.ACTION_PARAMETER_SCALAR) { return objectAdapter; } // hmmm... I think we should simply return null, as an indicator that there is no "pending" (see ScalarModelWithMultiPending) // // return an empty collection // // TODO: this should probably move down into OneToManyActionParameter impl // final OneToManyActionParameter otmap = (OneToManyActionParameter) actionParameter; // final CollectionSemantics collectionSemantics = otmap.getCollectionSemantics(); // final TypeOfFacet typeOfFacet = actionParameter.getFacet(TypeOfFacet.class); // final Class<?> elementType = typeOfFacet.value(); // final Object emptyCollection = collectionSemantics.emptyCollectionOf(elementType); // return scalarModel.getCurrentSession().getPersistenceSession().adapterFor(emptyCollection); return objectAdapter; } @Override public boolean isCollection(final ScalarModel scalarModel) { final ActionParameterMemento parameterMemento = scalarModel.getParameterMemento(); final ObjectActionParameter actionParameter = parameterMemento.getActionParameter(scalarModel.getSpecificationLoader()); return actionParameter.getFeatureType() == FeatureType.ACTION_PARAMETER_COLLECTION; } @Override public String toStringOf(final ScalarModel scalarModel) { return this.name() + ": " + scalarModel.getParameterMemento().toString(); } }; private static List<ObjectAdapter> choicesAsList(final ObjectAdapter[] choices) { if (choices != null && choices.length > 0) { return Arrays.asList(choices); } return Collections.emptyList(); } public abstract String getName(ScalarModel scalarModel); public abstract ObjectSpecification getScalarTypeSpec(ScalarModel scalarModel); public abstract String getIdentifier(ScalarModel scalarModel); public abstract String disable(ScalarModel scalarModel, Where where); public abstract String parseAndValidate(ScalarModel scalarModel, String proposedPojoAsStr); public abstract String validate(ScalarModel scalarModel, ObjectAdapter proposedAdapter); public abstract String getLongName(ScalarModel scalarModel); public abstract boolean isRequired(ScalarModel scalarModel); public abstract <T extends Facet> T getFacet(ScalarModel scalarModel, Class<T> facetType); static boolean isRequired(final FacetHolder facetHolder) { final MandatoryFacet mandatoryFacet = facetHolder.getFacet(MandatoryFacet.class); final boolean required = mandatoryFacet != null && !mandatoryFacet.isInvertedSemantics(); return required; } public abstract boolean hasChoices(ScalarModel scalarModel); public abstract List<ObjectAdapter> getChoices( final ScalarModel scalarModel, final ObjectAdapter[] argumentsIfAvailable, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory); public abstract boolean hasAutoComplete(ScalarModel scalarModel); public abstract List<ObjectAdapter> getAutoComplete( ScalarModel scalarModel, String searchArg, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory); public abstract int getAutoCompleteOrChoicesMinLength(ScalarModel scalarModel); public abstract void resetVersion(ScalarModel scalarModel); public abstract String getDescribedAs(ScalarModel scalarModel); public abstract Integer getLength(ScalarModel scalarModel); public abstract Integer getScale(ScalarModel scalarModel); public abstract int getTypicalLength(ScalarModel scalarModel); public abstract String getFileAccept(ScalarModel scalarModel); public abstract void init(ScalarModel scalarModel); public abstract void reset(ScalarModel scalarModel); public abstract ObjectAdapter load(final ScalarModel scalarModel); public abstract boolean isCollection(final ScalarModel scalarModel); public abstract String toStringOf(final ScalarModel scalarModel); } private final Kind kind; private final EntityModel parentEntityModel; @Override public ObjectAdapter load() { return kind.load(this); } private ObjectAdapter loadFromSuper() { return super.load(); } /** * Populated only if {@link #getKind()} is {@link Kind#PARAMETER} */ private ActionParameterMemento parameterMemento; /** * Populated only if {@link #getKind()} is {@link Kind#PROPERTY} */ private PropertyMemento propertyMemento; /** * Creates a model representing an action parameter of an action of a parent * object, with the {@link #getObject() value of this model} to be default * value (if any) of that action parameter. */ public ScalarModel(final EntityModel parentEntityModel, final ActionParameterMemento apm) { this.kind = Kind.PARAMETER; this.parentEntityModel = parentEntityModel; this.parameterMemento = apm; init(); setMode(Mode.EDIT); } /** * Creates a model representing a property of a parent object, with the * {@link #getObject() value of this model} to be current value of the * property. */ public ScalarModel(final EntityModel parentEntityModel, final PropertyMemento pm) { this.kind = Kind.PROPERTY; this.parentEntityModel = parentEntityModel; this.propertyMemento = pm; init(); getAndStore(parentEntityModel); setMode(Mode.VIEW); } private void init() { kind.init(this); } public void reset() { kind.reset(this); } public EntityModel getParentEntityModel() { return parentEntityModel; } private void getAndStore(final EntityModel parentEntityModel) { final ObjectAdapterMemento parentAdapterMemento = parentEntityModel.getObjectAdapterMemento(); final OneToOneAssociation property = propertyMemento.getProperty(getSpecificationLoader()); final ObjectAdapter parentAdapter = parentAdapterMemento.getObjectAdapter(ConcurrencyChecking.CHECK, getPersistenceSession(), getSpecificationLoader()); final ObjectAdapter associatedAdapter = property.get(parentAdapter, InteractionInitiatedBy.USER); setObject(associatedAdapter); } public boolean isCollection() { return kind.isCollection(this); } /** * Whether the scalar represents a {@link Kind#PROPERTY property} or a * {@link Kind#PARAMETER}. */ public Kind getKind() { return kind; } public String getName() { return kind.getName(this); } /** * Populated only if {@link #getKind()} is {@link Kind#PROPERTY} */ public PropertyMemento getPropertyMemento() { return propertyMemento; } /** * Populated only if {@link #getKind()} is {@link Kind#PARAMETER} */ public ActionParameterMemento getParameterMemento() { return parameterMemento; } /** * Overrides superclass' implementation, because a {@link ScalarModel} can * know the {@link ObjectSpecification of} the {@link ObjectAdapter adapter} * without there necessarily being any adapter being * {@link #setObject(ObjectAdapter) set}. */ @Override public ObjectSpecification getTypeOfSpecification() { return kind.getScalarTypeSpec(this); } public boolean isScalarTypeAnyOf(final Class<?>... requiredClass) { final String fullName = getTypeOfSpecification().getFullIdentifier(); for (final Class<?> requiredCls : requiredClass) { if (fullName.equals(requiredCls.getName())) { return true; } } return false; } public String getObjectAsString() { final ObjectAdapter adapter = getObject(); if (adapter == null) { return null; } return adapter.titleString(null); } @Override public void setObject(final ObjectAdapter adapter) { if(adapter == null) { super.setObject(null); return; } final Object pojo = adapter.getObject(); if(pojo == null) { super.setObject(null); return; } if(isCollection()) { final Iterable iterable = (Iterable) pojo; final ArrayList<ObjectAdapterMemento> listOfMementos = Lists.newArrayList(FluentIterable.from(iterable) .transform(ObjectAdapterMemento.Functions.fromPojo(getPersistenceSession())) .toList()); final ObjectAdapterMemento memento = ObjectAdapterMemento.createForList(listOfMementos, getTypeOfSpecification().getSpecId()); super.setObjectMemento(memento, getPersistenceSession(), getSpecificationLoader()); // associated value } else { super.setObject(adapter); // associated value } } public void setObjectAsString(final String enteredText) { // parse text to get adapter final ParseableFacet parseableFacet = getTypeOfSpecification().getFacet(ParseableFacet.class); if (parseableFacet == null) { throw new RuntimeException("unable to parse string for " + getTypeOfSpecification().getFullIdentifier()); } final ObjectAdapter adapter = parseableFacet.parseTextEntry(getObject(), enteredText, InteractionInitiatedBy.USER ); setObject(adapter); } public String disable(Where where) { return kind.disable(this, where); } public String validate(final ObjectAdapter proposedAdapter) { return kind.validate(this, proposedAdapter); } /** * Default implementation looks up from singleton, but can be overridden for * testing. */ protected AuthenticationSession getAuthenticationSession() { return getPersistenceSession().getServicesInjector().lookupService(AuthenticationSessionProvider.class).getAuthenticationSession(); } public boolean isRequired() { return kind.isRequired(this); } public String getLongName() { return kind.getLongName(this); } public <T extends Facet> T getFacet(final Class<T> facetType) { return kind.getFacet(this, facetType); } public String getDescribedAs() { return kind.getDescribedAs(this); } public String getFileAccept() { return kind.getFileAccept(this); } public boolean hasChoices() { return kind.hasChoices(this); } public List<ObjectAdapter> getChoices( final ObjectAdapter[] argumentsIfAvailable, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory) { return kind.getChoices(this, argumentsIfAvailable, authenticationSession, deploymentCategory); } public boolean hasAutoComplete() { return kind.hasAutoComplete(this); } public List<ObjectAdapter> getAutoComplete( final String searchTerm, final AuthenticationSession authenticationSession, final DeploymentCategory deploymentCategory) { return kind.getAutoComplete(this, searchTerm, authenticationSession, deploymentCategory); } /** * for {@link BigDecimal}s only. * * @see #getScale() */ public int getLength() { return kind.getLength(this); } /** * for {@link BigDecimal}s only. * * @see #getLength() */ public Integer getScale() { return kind.getScale(this); } /** * Additional links to render (if any) */ private List<LinkAndLabel> linkAndLabels = Lists.newArrayList(); @Override public List<LinkAndLabel> getLinks() { return Collections.unmodifiableList(linkAndLabels); } /** * @return */ public int getAutoCompleteMinLength() { return kind.getAutoCompleteOrChoicesMinLength(this); } /** * @return */ public ScalarModelWithPending asScalarModelWithPending() { return new ScalarModelWithPending(){ private static final long serialVersionUID = 1L; @Override public ObjectAdapterMemento getPending() { return ScalarModel.this.getPending(); } @Override public void setPending(ObjectAdapterMemento pending) { ScalarModel.this.setPending(pending); } @Override public ScalarModel getScalarModel() { return ScalarModel.this; } }; } /** * @return */ public ScalarModelWithMultiPending asScalarModelWithMultiPending() { return new ScalarModelWithMultiPending(){ private static final long serialVersionUID = 1L; @Override public ArrayList<ObjectAdapterMemento> getMultiPending() { final ObjectAdapterMemento pending = ScalarModel.this.getPending(); return pending != null ? pending.getList() : null; } @Override public void setMultiPending(final ArrayList<ObjectAdapterMemento> pending) { final ObjectAdapterMemento adapterMemento = ObjectAdapterMemento.createForList(pending, getScalarModel().getTypeOfSpecification().getSpecId()); ScalarModel.this.setPending(adapterMemento); } @Override public ScalarModel getScalarModel() { return ScalarModel.this; } }; } public PromptStyle getPromptStyle() { final PromptStyleFacet facet = getFacet(PromptStyleFacet.class); if(facet == null) { return null; } return facet.value() == PromptStyle.INLINE ? PromptStyle.INLINE : PromptStyle.DIALOG; } public boolean canEnterEditMode() { boolean editable = isEnabled(); return editable && isViewMode(); } boolean isEnabled() { Where where = getRenderingHint().isInTable() ? Where.PARENTED_TABLES : Where.OBJECT_FORMS; return disable(where) == null; } public String getReasonInvalidIfAny() { final OneToOneAssociation property = getPropertyMemento().getProperty(getSpecificationLoader()); final ObjectAdapter adapter = getParentEntityModel().load(); final ObjectAdapter associate = getObject(); final Consent validity = property.isAssociationValid(adapter, associate, InteractionInitiatedBy.USER); return validity.isAllowed() ? null : validity.getReason(); } /** * Apply changes to the underlying adapter (possibly returning a new adapter). * * @return adapter, which may be different from the original (if a {@link ViewModelFacet#isCloneable(Object) cloneable} view model, for example. */ public ObjectAdapter applyValue(ObjectAdapter adapter) { final OneToOneAssociation property = getPropertyMemento().getProperty(getSpecificationLoader()); // // previously there was a guard here to only apply changes provided: // // property.containsDoOpFacet(NotPersistedFacet.class) == null // // however, that logic is wrong; although a property may not be directly // persisted so far as JDO is concerned, it may be indirectly persisted // as the result of business logic in the setter. // // for example, see ExampleTaggableEntity (in isisaddons-module-tags). // // // previously (prior to XML layouts and 'single property' edits) we also used to check if // the property was disabled, using: // // if(property.containsDoOpFacet(DisabledFacet.class)) { // // skip, as per comments above // return; // } // // However, this would seem to be wrong, because the presence of a DisabledFacet doesn't necessarily mean // that the property is disabled (its disabledReason(...) might return null). // // In any case, the only code that calls this method already does the check, so think this is safe // to just remove. final ObjectAdapter associate = getObject(); property.set(adapter, associate, InteractionInitiatedBy.USER); final ViewModelFacet recreatableObjectFacet = adapter.getSpecification().getFacet(ViewModelFacet.class); if(recreatableObjectFacet != null) { final Object viewModel = adapter.getObject(); final boolean cloneable = recreatableObjectFacet.isCloneable(viewModel); if(cloneable) { final Object newViewModel = recreatableObjectFacet.clone(viewModel); adapter = getPersistenceSession().adapterFor(newViewModel); } } return adapter; } // ////////////////////////////////////// private FormExecutor formExecutor; /** * A hint passed from one Wicket UI component to another. * * Mot actually used by the model itself. */ public FormExecutor getFormExecutor() { return formExecutor; } public void setFormExecutor(final FormExecutor formExecutor) { this.formExecutor = formExecutor; } // ////////////////////////////////////// private InlinePromptContext inlinePromptContext; /** * Further hint, to support inline prompts... */ public InlinePromptContext getInlinePromptContext() { return inlinePromptContext; } public void setInlinePromptContext(InlinePromptContext inlinePromptContext) { if (this.inlinePromptContext != null) { // otherwise the components created for an property edit inline prompt will overwrite the original // components on the underlying page (which we go back to if the prompt is cancelled). return; } this.inlinePromptContext = inlinePromptContext; } // ////////////////////////////////////// // ////////////////////////////////////// /** * transient because only temporary hint. */ private transient ObjectAdapter[] actionArgsHint; public void setActionArgsHint(ObjectAdapter[] actionArgsHint) { this.actionArgsHint = actionArgsHint; } /** * The initial call of choicesXxx() for any given scalar argument needs the current values * of all args (possibly as initialized through a defaultNXxx(). */ public ObjectAdapter[] getActionArgsHint() { return actionArgsHint; } @Override public String toString() { return kind.toStringOf(this); } }