<commit_before>/*
 *  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.ui.panels;

import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.annotation.Nullable;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;

import org.apache.isis.applib.services.exceprecog.Category;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerService;
import org.apache.isis.applib.services.exceprecog.Recognition;
import org.apache.isis.applib.services.i18n.TranslationService;
import org.apache.isis.applib.services.iactnlayer.InteractionService;
import org.apache.isis.applib.services.message.MessageService;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.commons.internal.collections._Sets;
import org.apache.isis.core.interaction.session.MessageBroker;
import org.apache.isis.core.metamodel.facets.actions.redirect.RedirectFacet;
import org.apache.isis.core.metamodel.facets.properties.renderunchanged.UnchangingFacet;
import org.apache.isis.core.metamodel.spec.ManagedObject;
import org.apache.isis.core.metamodel.spec.ManagedObjects.EntityUtil;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.runtime.context.IsisAppCommonContext;
import org.apache.isis.core.runtime.memento.ObjectMemento;
import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
import org.apache.isis.viewer.wicket.model.models.ActionModel;
import org.apache.isis.viewer.wicket.model.models.EntityModel;
import org.apache.isis.viewer.wicket.model.models.FormExecutor;
import org.apache.isis.viewer.wicket.model.models.FormExecutorContext;
import org.apache.isis.viewer.wicket.model.models.ScalarModel;
import org.apache.isis.viewer.wicket.ui.components.scalars.blobclob.IsisBlobOrClobPanelAbstract;
import org.apache.isis.viewer.wicket.ui.errors.JGrowlUtil;
import org.apache.isis.viewer.wicket.ui.util.Components;

import lombok.NonNull;
import lombok.val;
import lombok.extern.log4j.Log4j2;

@Log4j2
public final class FormExecutorDefault<M extends FormExecutorContext>
implements FormExecutor {

    private static final long serialVersionUID = 1L;

    protected final M model;
    protected final WicketViewerSettings settings;
    private final FormExecutorStrategy<M> formExecutorStrategy;

    public FormExecutorDefault(final FormExecutorStrategy<M> formExecutorStrategy) {
        this.model = formExecutorStrategy.getModel();
        this.settings = getSettings();
        this.formExecutorStrategy = formExecutorStrategy;
    }

    /**
     *
     * @param page
     * @param targetIfAny
     * @param feedbackFormIfAny
     * @param withinPrompt
     *
     * @return <tt>false</tt> - if invalid args;
     * <tt>true</tt> if redirecting to new page, or repainting all components
     */
    @Override
    public boolean executeAndProcessResults(
            final Page page,
            final AjaxRequestTarget targetIfAny,
            final Form<?> feedbackFormIfAny,
            final boolean withinPrompt) {

        ManagedObject targetAdapter = null;

        final EntityModel targetEntityModel = model.getParentUiModel();
        val commonContext = targetEntityModel.getCommonContext();

        try {

            // may immediately throw a concurrency exception if
            // the Isis Oid held in the underlying EntityModel is stale w.r.t. the DB.
            targetAdapter = obtainTargetAdapter();

            // no concurrency exception, so continue...

            // validate the proposed property value/action arguments
            final Optional<Recognition> invalidReasonIfAny = getReasonInvalidIfAny();
            if (invalidReasonIfAny.isPresent()) {
                raiseWarning(targetIfAny, feedbackFormIfAny, invalidReasonIfAny.get());
                return false; // invalid args, stay on page
            }

            //
            // the following line will (attempt to) invoke the action, and will in turn either:
            //
            // 1. return a non-null result from a successful invocation
            //
            // 2. return a null result (from a successful action returning void)
            //
            // 3. throws a RuntimeException, either:
            //    a) as result of application throwing RecoverableException (DN transaction still intact)
            //    b) as result of DB exception, eg uniqueness constraint violation (DN transaction marked to abort)
            //
            // (The DB exception might actually be thrown by the flush() that follows.
            //
            val resultAdapter = obtainResultAdapter();

            val redirectFacet =  model instanceof ActionModel
                ? ((ActionModel) model).getMetaModel().getFacet(RedirectFacet.class)
                : null;

            if(commonContext.getInteractionLayerTracker().isInInteraction()) {

                // flush any queued changes; any concurrency or violation exceptions will actually be thrown here
                commonContext.getTransactionService().flushTransaction();

                // TODO: REVIEW: I wonder why this next block is only performed within the outer if block?
                //  my guess is that the if block is always evaluated, in which case this is always run.

                if(willDefinitelyRedirect(redirectFacet)) {
                    // should circuit the redirect check later on; there's no need to reset the adapter
                    // (this also provides a workaround for view models wrapping now-deleted entities)
                    targetAdapter = ManagedObject.empty(targetAdapter.getSpecification());
                } else if(EntityUtil.isDetachedOrRemoved(targetAdapter)) {
                    // if this was an entity delete action
                    // then we don't re-fetch / re-create the targetAdapter
                    targetAdapter = ManagedObject.empty(targetAdapter.getSpecification());
                } else {
                    // update target, since version updated
                    targetAdapter = targetEntityModel.getManagedObject();
                    targetEntityModel.resetPropertyModels();
                }
            }

            // hook to close prompt etc.
            onExecuteAndProcessResults(targetIfAny);

            if (shouldRedirect(targetAdapter, resultAdapter, redirectFacet)
                    || hasBlobsOrClobs(page)
                    || targetIfAny == null) {

                redirectTo(resultAdapter, targetIfAny);

            } else {

                // in this branch the result must be same "logical" object as target, but
                // the OID might have changed if a view model.
                if (resultAdapter != null && targetAdapter != resultAdapter) {
                    targetEntityModel.setObject(resultAdapter);
                    targetAdapter = targetEntityModel.getManagedObject();
                }
                if(!EntityUtil.isDetachedOrRemoved(targetAdapter)) {
                    targetEntityModel.resetPropertyModels();
                }

                // also in this branch we also know that there *is* an ajax target to use
                addComponentsToRedraw(targetIfAny);
<commit_msg>ISIS-2778: ensures that target adapter has been injected into prior to resetting properties.<commit_after>/*
 *  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.ui.panels;

import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.annotation.Nullable;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;

import org.apache.isis.applib.services.exceprecog.Category;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerService;
import org.apache.isis.applib.services.exceprecog.Recognition;
import org.apache.isis.applib.services.i18n.TranslationService;
import org.apache.isis.applib.services.iactnlayer.InteractionService;
import org.apache.isis.applib.services.message.MessageService;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.commons.internal.collections._Sets;
import org.apache.isis.core.interaction.session.MessageBroker;
import org.apache.isis.core.metamodel.facets.actions.redirect.RedirectFacet;
import org.apache.isis.core.metamodel.facets.properties.renderunchanged.UnchangingFacet;
import org.apache.isis.core.metamodel.spec.ManagedObject;
import org.apache.isis.core.metamodel.spec.ManagedObjects.EntityUtil;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.runtime.context.IsisAppCommonContext;
import org.apache.isis.core.runtime.memento.ObjectMemento;
import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
import org.apache.isis.viewer.wicket.model.models.ActionModel;
import org.apache.isis.viewer.wicket.model.models.EntityModel;
import org.apache.isis.viewer.wicket.model.models.FormExecutor;
import org.apache.isis.viewer.wicket.model.models.FormExecutorContext;
import org.apache.isis.viewer.wicket.model.models.ScalarModel;
import org.apache.isis.viewer.wicket.ui.components.scalars.blobclob.IsisBlobOrClobPanelAbstract;
import org.apache.isis.viewer.wicket.ui.errors.JGrowlUtil;
import org.apache.isis.viewer.wicket.ui.util.Components;

import lombok.NonNull;
import lombok.val;
import lombok.extern.log4j.Log4j2;

@Log4j2
public final class FormExecutorDefault<M extends FormExecutorContext>
implements FormExecutor {

    private static final long serialVersionUID = 1L;

    protected final M model;
    protected final WicketViewerSettings settings;
    private final FormExecutorStrategy<M> formExecutorStrategy;

    public FormExecutorDefault(final FormExecutorStrategy<M> formExecutorStrategy) {
        this.model = formExecutorStrategy.getModel();
        this.settings = getSettings();
        this.formExecutorStrategy = formExecutorStrategy;
    }

    /**
     *
     * @param page
     * @param targetIfAny
     * @param feedbackFormIfAny
     * @param withinPrompt
     *
     * @return <tt>false</tt> - if invalid args;
     * <tt>true</tt> if redirecting to new page, or repainting all components
     */
    @Override
    public boolean executeAndProcessResults(
            final Page page,
            final AjaxRequestTarget targetIfAny,
            final Form<?> feedbackFormIfAny,
            final boolean withinPrompt) {

        ManagedObject targetAdapter = null;

        final EntityModel targetEntityModel = model.getParentUiModel();
        val commonContext = targetEntityModel.getCommonContext();

        try {

            // may immediately throw a concurrency exception if
            // the Isis Oid held in the underlying EntityModel is stale w.r.t. the DB.
            targetAdapter = obtainTargetAdapter();

            // no concurrency exception, so continue...

            // validate the proposed property value/action arguments
            final Optional<Recognition> invalidReasonIfAny = getReasonInvalidIfAny();
            if (invalidReasonIfAny.isPresent()) {
                raiseWarning(targetIfAny, feedbackFormIfAny, invalidReasonIfAny.get());
                return false; // invalid args, stay on page
            }

            //
            // the following line will (attempt to) invoke the action, and will in turn either:
            //
            // 1. return a non-null result from a successful invocation
            //
            // 2. return a null result (from a successful action returning void)
            //
            // 3. throws a RuntimeException, either:
            //    a) as result of application throwing RecoverableException (DN transaction still intact)
            //    b) as result of DB exception, eg uniqueness constraint violation (DN transaction marked to abort)
            //
            // (The DB exception might actually be thrown by the flush() that follows.
            //
            val resultAdapter = obtainResultAdapter();

            val redirectFacet =  model instanceof ActionModel
                ? ((ActionModel) model).getMetaModel().getFacet(RedirectFacet.class)
                : null;

            if(commonContext.getInteractionLayerTracker().isInInteraction()) {

                // flush any queued changes; any concurrency or violation exceptions will actually be thrown here
                commonContext.getTransactionService().flushTransaction();

                // TODO: REVIEW: I wonder why this next block is only performed within the outer if block?
                //  my guess is that the if block is always evaluated, in which case this is always run.

                if(willDefinitelyRedirect(redirectFacet)) {
                    // should circuit the redirect check later on; there's no need to reset the adapter
                    // (this also provides a workaround for view models wrapping now-deleted entities)
                    targetAdapter = ManagedObject.empty(targetAdapter.getSpecification());
                } else if(EntityUtil.isDetachedOrRemoved(targetAdapter)) {
                    // if this was an entity delete action
                    // then we don't re-fetch / re-create the targetAdapter
                    targetAdapter = ManagedObject.empty(targetAdapter.getSpecification());
                } else {
                    // update target, since version updated
                    targetAdapter = targetEntityModel.getManagedObject();
                    targetEntityModel.resetPropertyModels();
                }
            }

            // hook to close prompt etc.
            onExecuteAndProcessResults(targetIfAny);

            if (shouldRedirect(targetAdapter, resultAdapter, redirectFacet)
                    || hasBlobsOrClobs(page)
                    || targetIfAny == null) {

                redirectTo(resultAdapter, targetIfAny);

            } else {

                // in this branch the result must be same "logical" object as target, but
                // the OID might have changed if a view model.
                if (resultAdapter != null && targetAdapter != resultAdapter) {
                    targetEntityModel.setObject(resultAdapter);
                    targetAdapter = targetEntityModel.getManagedObject();
                }
                if(!EntityUtil.isDetachedOrRemoved(targetAdapter)) {
                    if(targetAdapter != null) {
                        getCommonContext().injectServicesInto(targetAdapter.getPojo());
                    }
                    targetEntityModel.resetPropertyModels();
                }

                // also in this branch we also know that there *is* an ajax target to use
                addComponentsToRedraw(targetIfAny);

{'fraction_non_alphanumeric': 0.0804986642920748, 'fraction_numerical': 0.002018403086969427, 'mean_word_length': 2.6566095072715434, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5568396', 'n_tokens_mistral': 4439, 'n_tokens_neox': 4266, 'n_words': 1189}