<commit_before>/*
 * Copyright 2002-2004 the original author or authors.
 *
 * Licensed 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.springframework.binding.form.support;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.binding.MutablePropertyAccessStrategy;
import org.springframework.binding.PropertyAccessStrategy;
import org.springframework.binding.PropertyMetadataAccessStrategy;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.Converter;
import org.springframework.binding.convert.support.DefaultConversionService;
import org.springframework.binding.convert.support.GenericConversionService;
import org.springframework.binding.form.CommitListener;
import org.springframework.binding.form.ConfigurableFormModel;
import org.springframework.binding.form.FieldFace;
import org.springframework.binding.form.FieldFaceSource;
import org.springframework.binding.form.FieldMetadata;
import org.springframework.binding.form.FormModel;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.binding.support.BeanPropertyAccessStrategy;
import org.springframework.binding.value.CommitTrigger;
import org.springframework.binding.value.ValueModel;
import org.springframework.binding.value.support.AbstractPropertyChangePublisher;
import org.springframework.binding.value.support.BufferedValueModel;
import org.springframework.binding.value.support.DirtyTrackingValueModel;
import org.springframework.binding.value.support.MethodInvokingDerivedValueModel;
import org.springframework.binding.value.support.TypeConverter;
import org.springframework.binding.value.support.ValueHolder;
import org.springframework.richclient.application.ApplicationServicesLocator;
import org.springframework.richclient.util.Assert;
import org.springframework.richclient.util.ClassUtils;
import org.springframework.richclient.util.EventListenerListHelper;
import org.springframework.util.CachingMapDecorator;

/**
 * Base implementation of HierarchicalFormModel and ConfigurableFormModel
 * subclasses need only implement the 4 value model interception methods.
 *
 * @author <NAME>
 * @author <NAME>
 */
public abstract class AbstractFormModel extends AbstractPropertyChangePublisher implements HierarchicalFormModel,
		ConfigurableFormModel {

	private String id;

	private final FormModelMediatingValueModel formObjectHolder;

	private final MutablePropertyAccessStrategy propertyAccessStrategy;

	private HierarchicalFormModel parent;

	private final List children = new ArrayList();

	private boolean buffered = false;

	private boolean enabled = true;

	private boolean oldEnabled = true;

	private boolean readOnly = false;

	private boolean oldReadOnly = false;

	private boolean authorized = true;
	
	private boolean oldDirty;

	private boolean oldCommittable = true;

	private ConversionService conversionService;

	private final CommitTrigger commitTrigger = new CommitTrigger();

	private final Map mediatingValueModels = new HashMap();

	private final Map propertyValueModels = new HashMap();

	private final Map convertingValueModels = new HashMap();

	private final Map fieldMetadata = new HashMap();

	private final Set dirtyValueAndFormModels = new HashSet();

	private final Map propertyConversionServices = new CachingMapDecorator() {
		public Object create(Object key) {
			return new DefaultConversionService() {
				protected void addDefaultConverters() {
				}
			};
		}
	};

	private FieldFaceSource fieldFaceSource;

	protected final PropertyChangeListener parentStateChangeHandler = new ParentStateChangeHandler();

	protected final PropertyChangeListener childStateChangeHandler = new ChildStateChangeHandler();

	private final EventListenerListHelper commitListeners = new EventListenerListHelper(CommitListener.class);

	private Class defaultInstanceClass;

	protected AbstractFormModel() {
		this(new ValueHolder());
	}

	protected AbstractFormModel(Object domainObject) {
		this(new ValueHolder(domainObject), true);
	}

	public AbstractFormModel(Object domainObject, boolean buffered) {
		this(new ValueHolder(domainObject), buffered);
	}

	protected AbstractFormModel(ValueModel domainObjectHolder, boolean buffered) {
		this(new BeanPropertyAccessStrategy(domainObjectHolder), buffered);
	}

	protected AbstractFormModel(MutablePropertyAccessStrategy propertyAccessStrategy, boolean buffered) {
		ValueModel domainObjectHolder = propertyAccessStrategy.getDomainObjectHolder();
		prepareValueModel(domainObjectHolder);
		this.formObjectHolder = new FormModelMediatingValueModel(domainObjectHolder, false);
		this.propertyAccessStrategy = propertyAccessStrategy;
		this.buffered = buffered;
		if (domainObjectHolder.getValue() != null)
			this.defaultInstanceClass = domainObjectHolder.getValue().getClass();
	}

	/**
	 * Prepare the provided value model for use in this form model.
	 * @param valueModel to prepare
	 */
	protected void prepareValueModel(ValueModel valueModel) {
		if (valueModel instanceof BufferedValueModel) {
			((BufferedValueModel) valueModel).setCommitTrigger(commitTrigger);
		}

		// If the value model that we were built on is "dirty trackable" then we
		// need to monitor it for changes in its dirty state
		if (valueModel instanceof DirtyTrackingValueModel) {
			((DirtyTrackingValueModel) valueModel).addPropertyChangeListener(DIRTY_PROPERTY, childStateChangeHandler);
		}
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Object getFormObject() {
		return getFormObjectHolder().getValue();
	}

	public void setFormObject(Object formObject) {
		setDeliverValueChangeEvents(false, false);
		if (formObject == null) {
			handleSetNullFormObject();
		}
		else {
			getFormObjectHolder().setValue(formObject);
			setEnabled(true);
		}
		// this will cause all buffered value models to revert
		// to the new form objects property values
		commitTrigger.revert();
		setDeliverValueChangeEvents(true, true);
	}

	/**
	 * Disconnect view from data in MediatingValueModels, possibly clearing them
	 * afterwards.
	 *
	 * @param deliverValueChangeEvents <code>true</code> if events should be
	 * delivered.
	 * @param clearValueModels <code>true</code> if models should be cleared
	 * afterwards.
	 */
	private void setDeliverValueChangeEvents(boolean deliverValueChangeEvents, boolean clearValueModels) {
		formObjectHolder.setDeliverValueChangeEvents(deliverValueChangeEvents);
		for (Iterator i = mediatingValueModels.values().iterator(); i.hasNext();) {
			FormModelMediatingValueModel valueModel = (FormModelMediatingValueModel) i.next();
			valueModel.setDeliverValueChangeEvents(deliverValueChangeEvents);
		}
		if (clearValueModels)
		{
			for (Iterator i = mediatingValueModels.values().iterator(); i.hasNext();) {
				FormModelMediatingValueModel valueModel = (FormModelMediatingValueModel) i.next();
				valueModel.clearDirty();
			}
		}
	}

	public void setDefaultInstanceClass(Class defaultInstanceClass) {
		this.defaultInstanceClass = defaultInstanceClass;
	}

	public Class getDefaultInstanceClass() {
		return defaultInstanceClass;
	}

	protected void handleSetNullFormObject() {<commit_msg>Revert fix for RCP-631 as it is under discussion.<commit_after>/*
 * Copyright 2002-2004 the original author or authors.
 *
 * Licensed 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.springframework.binding.form.support;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.binding.MutablePropertyAccessStrategy;
import org.springframework.binding.PropertyAccessStrategy;
import org.springframework.binding.PropertyMetadataAccessStrategy;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.Converter;
import org.springframework.binding.convert.support.DefaultConversionService;
import org.springframework.binding.convert.support.GenericConversionService;
import org.springframework.binding.form.CommitListener;
import org.springframework.binding.form.ConfigurableFormModel;
import org.springframework.binding.form.FieldFace;
import org.springframework.binding.form.FieldFaceSource;
import org.springframework.binding.form.FieldMetadata;
import org.springframework.binding.form.FormModel;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.binding.support.BeanPropertyAccessStrategy;
import org.springframework.binding.value.CommitTrigger;
import org.springframework.binding.value.ValueModel;
import org.springframework.binding.value.support.AbstractPropertyChangePublisher;
import org.springframework.binding.value.support.BufferedValueModel;
import org.springframework.binding.value.support.DirtyTrackingValueModel;
import org.springframework.binding.value.support.MethodInvokingDerivedValueModel;
import org.springframework.binding.value.support.TypeConverter;
import org.springframework.binding.value.support.ValueHolder;
import org.springframework.richclient.application.ApplicationServicesLocator;
import org.springframework.richclient.util.Assert;
import org.springframework.richclient.util.ClassUtils;
import org.springframework.richclient.util.EventListenerListHelper;
import org.springframework.util.CachingMapDecorator;

/**
 * Base implementation of HierarchicalFormModel and ConfigurableFormModel
 * subclasses need only implement the 4 value model interception methods.
 *
 * @author <NAME>
 * @author <NAME>
 */
public abstract class AbstractFormModel extends AbstractPropertyChangePublisher implements HierarchicalFormModel,
		ConfigurableFormModel {

	private String id;

	private final FormModelMediatingValueModel formObjectHolder;

	private final MutablePropertyAccessStrategy propertyAccessStrategy;

	private HierarchicalFormModel parent;

	private final List children = new ArrayList();

	private boolean buffered = false;

	private boolean enabled = true;

	private boolean oldEnabled = true;

	private boolean readOnly = false;

	private boolean oldReadOnly = false;

	private boolean authorized = true;
	
	private boolean oldDirty;

	private boolean oldCommittable = true;

	private ConversionService conversionService;

	private final CommitTrigger commitTrigger = new CommitTrigger();

	private final Map mediatingValueModels = new HashMap();

	private final Map propertyValueModels = new HashMap();

	private final Map convertingValueModels = new HashMap();

	private final Map fieldMetadata = new HashMap();

	private final Set dirtyValueAndFormModels = new HashSet();

	private final Map propertyConversionServices = new CachingMapDecorator() {
		public Object create(Object key) {
			return new DefaultConversionService() {
				protected void addDefaultConverters() {
				}
			};
		}
	};

	private FieldFaceSource fieldFaceSource;

	protected final PropertyChangeListener parentStateChangeHandler = new ParentStateChangeHandler();

	protected final PropertyChangeListener childStateChangeHandler = new ChildStateChangeHandler();

	private final EventListenerListHelper commitListeners = new EventListenerListHelper(CommitListener.class);

	private Class defaultInstanceClass;

	protected AbstractFormModel() {
		this(new ValueHolder());
	}

	protected AbstractFormModel(Object domainObject) {
		this(new ValueHolder(domainObject), true);
	}

	public AbstractFormModel(Object domainObject, boolean buffered) {
		this(new ValueHolder(domainObject), buffered);
	}

	protected AbstractFormModel(ValueModel domainObjectHolder, boolean buffered) {
		this(new BeanPropertyAccessStrategy(domainObjectHolder), buffered);
	}

	protected AbstractFormModel(MutablePropertyAccessStrategy propertyAccessStrategy, boolean buffered) {
		ValueModel domainObjectHolder = propertyAccessStrategy.getDomainObjectHolder();
		prepareValueModel(domainObjectHolder);
		this.formObjectHolder = new FormModelMediatingValueModel(domainObjectHolder, false);
		this.propertyAccessStrategy = propertyAccessStrategy;
		this.buffered = buffered;
		if (domainObjectHolder.getValue() != null)
			this.defaultInstanceClass = domainObjectHolder.getValue().getClass();
	}

	/**
	 * Prepare the provided value model for use in this form model.
	 * @param valueModel to prepare
	 */
	protected void prepareValueModel(ValueModel valueModel) {
		if (valueModel instanceof BufferedValueModel) {
			((BufferedValueModel) valueModel).setCommitTrigger(commitTrigger);
		}

		// If the value model that we were built on is "dirty trackable" then we
		// need to monitor it for changes in its dirty state
		if (valueModel instanceof DirtyTrackingValueModel) {
			((DirtyTrackingValueModel) valueModel).addPropertyChangeListener(DIRTY_PROPERTY, childStateChangeHandler);
		}
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Object getFormObject() {
		return getFormObjectHolder().getValue();
	}

	public void setFormObject(Object formObject) {
		setDeliverValueChangeEvents(false, false);
		if (formObject == null) {
			handleSetNullFormObject();
		}
		else {
			getFormObjectHolder().setValue(formObject);
			setEnabled(true);
		}
		// this will cause all buffered value models to revert
		// to the new form objects property values
		commitTrigger.revert();
		setDeliverValueChangeEvents(true, true);
	}

	/**
	 * Disconnect view from data in MediatingValueModels, possibly clearing them
	 * afterwards.
	 *
	 * @param deliverValueChangeEvents <code>true</code> if events should be
	 * delivered.
	 * @param clearValueModels <code>true</code> if models should be cleared
	 * afterwards.
	 */
	private void setDeliverValueChangeEvents(boolean deliverValueChangeEvents, boolean clearValueModels) {
		formObjectHolder.setDeliverValueChangeEvents(deliverValueChangeEvents);
		for (Iterator i = mediatingValueModels.values().iterator(); i.hasNext();) {
			FormModelMediatingValueModel valueModel = (FormModelMediatingValueModel) i.next();
			valueModel.setDeliverValueChangeEvents(deliverValueChangeEvents);
			if (clearValueModels)
				valueModel.clearDirty();
		}
	}

	public void setDefaultInstanceClass(Class defaultInstanceClass) {
		this.defaultInstanceClass = defaultInstanceClass;
	}

	public Class getDefaultInstanceClass() {
		return defaultInstanceClass;
	}

	protected void handleSetNullFormObject() {
{'fraction_non_alphanumeric': 0.07779446739266492, 'fraction_numerical': 0.0018149956189760922, 'mean_word_length': 6.005260850504165, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, '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': '2376415', 'n_tokens_mistral': 4279, 'n_tokens_neox': 3976, 'n_words': 1097}