Skip to main content
summaryrefslogtreecommitdiffstats
blob: fda203872117f61a0b590e1614ea40ecc9f8e479 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*******************************************************************************
 * Copyright (c) 2010, 2015 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0, which accompanies this distribution
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 *
 * Contributors:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.jaxb.core.internal.context;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import org.eclipse.jpt.common.utility.internal.ObjectTools;
import org.eclipse.jpt.common.utility.internal.collection.CollectionTools;
import org.eclipse.jpt.jaxb.core.context.JaxbContextNode;

/**
 * Utility methods for manipulating context containers.
 * (Copied originally from *.jpa.core on 11/6/12 - pwf)
 */
public class ContextContainerTools {
	
	/**
	 * Adapter used to synchronize a context container with its corresponding
	 * resource container.
	 * 
	 * @param <C> the type of context elements
	 * @param <R> the type of resource elements
	 */
	public interface Adapter<C extends JaxbContextNode, R> {
		
		/**
		 * Return the container's context elements.
		 */
		Iterable<C> getContextElements();
		
		/**
		 * Return the container's current set of resource elements.
		 * These are what the context elements will be synchronized to.
		 */
		Iterable<R> getResourceElements();
		
		/**
		 * Return the resource element corresponding to the specified context
		 * element.
		 */
		R getResourceElement(C contextElement);
		
		/**
		 * Move the specified context element to the specified index.
		 */
		void moveContextElement(int index, C element);
		
		/**
		 * Add a context element for the specified resource element at the
		 * specified index.
		 */
		void addContextElement(int index, R resourceElement);
		
		/**
		 * Remove the specified context element from the container.
		 */
		void removeContextElement(C element);
	}
	
	
	/**
	 * Using the specified adapter, synchronize a context container with its
	 * corresponding resource container: moving, removing, and adding elements
	 * as necessary.
	 * <p>
	 * We can do this because:<ul>
	 * <li>the XML translators will <em>move</em> the EMF elements when
	 * appropriate (as opposed to simply rebuilding them in place).
	 * <li>the Java resource model will re-use existing annotations when
	 * appropriate (as opposed to simply rebuilding them in place).
	 * </ul>
	 */
	public static <C extends JaxbContextNode, R> void synchronizeWithResourceModel(Adapter<C, R> adapter) {
		sync(adapter, true);  // true = sync
	}
	
	/**
	 * @see #synchronizeWithResourceModel(Adapter)
	 */
	public static <C extends JaxbContextNode, R> void update(Adapter<C, R> adapter) {
		sync(adapter, false);  // false = update
	}
	
	/**
	 * The specified <code>sync</code> flag controls whether any surviving
	 * context nodes are either <em>synchronized</em> (<code>true</code>) or
	 * <em>updated</em> (<code>false</code>).
	 */
	protected static <C extends JaxbContextNode, R> void sync(Adapter<C, R> adapter, boolean sync) {
		HashSet<C> contextElements = CollectionTools.hashSet(adapter.getContextElements());
		ArrayList<C> contextElementsToSync = new ArrayList<C>(contextElements.size());
		int resourceIndex = 0;
		
		for (R resourceElement : adapter.getResourceElements()) {
			boolean match = false;
			for (Iterator<C> stream = contextElements.iterator(); stream.hasNext(); ) {
				C contextElement = stream.next();
				if (ObjectTools.equals(adapter.getResourceElement(contextElement), resourceElement)) {
					// we don't know the source index because the element has been moved by previously moved elements
					adapter.moveContextElement(resourceIndex, contextElement);
					stream.remove();
					// TODO perform update here someday...
					contextElementsToSync.add(contextElement);
					match = true;
					break;
				}
			}
			if ( ! match) {
				// added elements are sync'ed during construction or will be
				// updated during the next "update" (which is triggered by
				// their addition to the model)
				adapter.addContextElement(resourceIndex, resourceElement);
			}
			resourceIndex++;
		}
		// remove any leftover context elements
		for (C contextElement : contextElements) {
			adapter.removeContextElement(contextElement);
		}
		// TODO bjv
		// take care of the structural changes before sync'ing the remaining elements;
		// we might be able to do this inline once we get rid of the "list change" events
		// and go with only add, remove, etc. list events
		// (these syncs will trigger "list change" events with list aspect adapters, which
		// trigger refreshes of UI adapters (e.g. TableModelAdapter) which will prematurely
		// discover any structural changes... :( )
		// see ItemAspectListValueModelAdapter.itemAspectChanged(EventObject)
		for (C contextElement : contextElementsToSync) {
			if (sync) {
				contextElement.synchronizeWithResourceModel();
			} else {
				contextElement.update();
			}
		}
	}
	
	private ContextContainerTools() {
		super();
		throw new UnsupportedOperationException();
	}
}

Back to the top