/*
 * Copyright (c) 2009-2013 Topi Nieminen
 */
package fi.tnie.db.model.cm;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

public class ChangeSet
	implements Proposition {	

	private int rejected = 0;
	private boolean submitted;
	private boolean committed;
	
	private Map<ConstrainedValueModel<?>, Proposition> propositionMap;
	
	public <V> Proposition add(final ConstrainedValueModel<V> model, V proposed) {
		return add(model, proposed, false);  
	}
	
	public <V> Proposition submit(final ConstrainedValueModel<V> model, V proposed) {
		return add(model, proposed, true);  
	}
		
	private <V> Proposition add(final ConstrainedValueModel<V> model, V proposed, boolean submit) {		
		Proposition p = model.propose(this, proposed);
		getPropositionMap().put(model, p);
		
		if (submit) {
			model.submit(this, p);
		}
		
		return p;
	}
	
	@Override
	public void commit() {
		for (Proposition p : getPropositionMap().values()) {
			p.commit();					
		}
		
		committed = true;
	}

	@Override
	public boolean isRejected() {
		return this.rejected > 0;
	}
	
	/**
	 * Attempts to apply this change set as a whole.
	 *  
	 * @return
	 */
	
	public boolean apply() {		
		boolean committed = false;
		submit(this);
		
		if (!isRejected()) {
			commit();
			committed = true;
		}
		
		return committed;
	}
	
	public void submit() {
		submit(this);
	}

	@Override
	public void submit(ChangeSet cs) {
		// Calls to ConstrainedValueModel.submit(ChangeSet cs, Proposition p)
		// may cause the dependent models to be added in this change-set.
		// By taking a snapshot of the key-set we avoid   
		// ConcurrentModificationExceptions:
		
		Map<ConstrainedValueModel<?>, Proposition> pm = getPropositionMap();
		
		ArrayList<ConstrainedValueModel<?>> keys = 
			new ArrayList<ConstrainedValueModel<?>>(pm.keySet());
				
		for (ConstrainedValueModel<?> m : keys) {
			Proposition p = pm.get(m);						
			m.submit(cs, p);
						
			if (p.isRejected()) {
				this.rejected++;
			}
		}
		
		this.submitted = true;
	}

	@Override
	public boolean isCommitted() {
		return this.committed; 
	}

	@Override
	public boolean isSubmitted() {		
		return this.submitted;
	}

	@Override
	public void reject() {
		this.rejected++;
	}
	
	public Map<ConstrainedValueModel<?>, Proposition> getPropositionMap() {
		if (propositionMap == null) {
			propositionMap = new LinkedHashMap<ConstrainedValueModel<?>, Proposition>();			
		}

		return propositionMap;
	}
}

@@ -365,130 +365,450 @@
 %0A%09%0A%09
-public %3CV%3E Proposition add(final ConstrainedValueModel%3CV%3E model, V proposed) %7B%0A%09%09return add(model, proposed, false);  %0A%09%7D%0A
+/**%0A%09 * Creates proposition %3Ccode%3Eproposed%3C/code%3E value and adds it to this change set.%0A%09 * %0A%09 * @param %3CV%3E%0A%09 * @param model%0A%09 * @param proposed%0A%09 * @return%0A%09 */%0A%09public %3CV%3E Proposition add(final ConstrainedValueModel%3CV%3E model, V proposed) %7B%0A%09%09return add(model, proposed, false);  %0A%09%7D%0A%09%0A%09/**%0A%09 * Creates and adds proposition from %3Ccode%3Eproposed%3C/code%3E value and submits it.%0A%09 * @param %3CV%3E%0A%09 * @param model%0A%09 * @param proposed%0A%09 * @return%0A%09 */
 %09%0A%09p
@@ -2231,20 +2231,16 @@
 et(m);%09%09
-%09%09%09%09
 %0A%09%09%09m.su

Add some documentation