View Javadoc

1   /*
2    * Created on Jul 30, 2005. Copyright 1999-2006 Faculty of Mathematics, Physics
3    * and Informatics, Comenius University, Bratislava. This file is protected by
4    * the Mozilla Public License version 1.1 (the "License"); you may not use this
5    * file except in compliance with the License. You may obtain a copy of the
6    * License at http://euromath2.sourceforge.net/license.html Unless required by
7    * applicable law or agreed to in writing, software distributed under the
8    * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
9    * OF ANY KIND, either express or implied. See the License for the specific
10   * language governing permissions and limitations under the License.
11   */
12  package sk.uniba.euromath.gene;
13  import java.io.File;
14  import java.io.IOException;
15  import java.util.ArrayList;
16  import java.util.EnumSet;
17  import java.util.HashMap;
18  import java.util.HashSet;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Set;
22  import javax.xml.transform.Result;
23  import javax.xml.transform.Source;
24  import javax.xml.transform.dom.DOMResult;
25  import javax.xml.transform.dom.DOMSource;
26  import javax.xml.transform.stream.StreamResult;
27  import org.apache.commons.lang.StringUtils;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.DocumentFragment;
30  import org.w3c.dom.Element;
31  import org.w3c.dom.Node;
32  import org.w3c.dom.ProcessingInstruction;
33  import sk.baka.ikslibs.ObjectSource;
34  import sk.baka.ikslibs.ResultEnum;
35  import sk.baka.ikslibs.SourceEnum;
36  import sk.baka.ikslibs.TransformUtils;
37  import sk.baka.ikslibs.modify.DOMChangeCollector;
38  import sk.baka.ikslibs.modify.IChangeCollector;
39  import sk.baka.ikslibs.modify.ObjectChangeCollector;
40  import sk.baka.ikslibs.splitted.SplittedDocHolder;
41  import sk.baka.xml.gene.CoordinatorInfo;
42  import sk.baka.xml.gene.CoordinatorInputKey;
43  import sk.baka.xml.gene.ExportContext;
44  import sk.baka.xml.gene.ExportException;
45  import sk.baka.xml.gene.ExportUtils;
46  import sk.baka.xml.gene.IConfigurator;
47  import sk.baka.xml.gene.ICoordinator;
48  import sk.baka.xml.gene.IExporter;
49  import sk.baka.xml.gene.controller.NamespacePath;
50  import sk.baka.xml.gene.exportgraph.ExportGraphBuilder;
51  import sk.baka.xml.gene.exportgraph.TransformGraph;
52  import sk.uniba.euromath.document.XMLAccess;
53  import sk.uniba.euromath.editor.EditorException;
54  import sk.uniba.euromath.editor.EditorSite;
55  import sk.uniba.euromath.editor.IInputPreprocessor;
56  import sk.uniba.euromath.editor.IRenderer;
57  import sk.uniba.euromath.editor.RendererInfo;
58  import sk.uniba.euromath.editor.RendererSelector;
59  /***
60   * <p>
61   * Acts as a GENE coordinator. Collects GENE output, instantiates and
62   * initializes renderers, and fires events on document change.
63   * </p>
64   * <h3>Implementation of GENE output handler</h3>
65   * <p>
66   * The object produces a special tree (using the
67   * {@link Coordinator#partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}
68   * function), that is processed by the object itself on
69   * {@link Coordinator#finishExport()} function. This tree is intended to capture
70   * the tree structure of nametrees in output document (and renderers, rendering
71   * these nametrees).
72   * </p>
73   * <p>
74   * Each
75   * {@link Coordinator#partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}
76   * constructs (or updates) a renderer, associates it with the
77   * {@link CoordinatorInputKey key} and produces some elements. These elements
78   * must be able to provide:
79   * </p>
80   * <ul>
81   * <li>a mapping from GENE-providen ID (given to the renderer via the
82   * {@link SplittedDocHolder#PI_GENEREF_TARGET gene-ref PI} to the key (we must be able to provide child
83   * renderers to a renderer, that uses ID as their identification key), and</li>
84   * <li>a parent-child relation for renderers, to verify renderers requirements
85   * for child renderers.</li>
86   * </ul>
87   * <p>
88   * These nodes are produced:
89   * </p>
90   * <ul>
91   * <li><code>ref</code> element - serves for identification of the renderer.
92   * Contains attribute <code>id</code> that uniquely identifies key in the
93   * renderer tree.</li>
94   * <li>We must store ids for each child nametree. Thus, each
95   * {@link SplittedDocHolder#PI_GENEREF_TARGET gene-ref PI} is copied and placed in
96   * <code>child-renderer</code>. This {@link #CHILD_RENDERER_ELEMENT_NAME} element
97   * will contain ID attribute - a copy of gene-ref PI value.</li>
98   * </ul>
99   * <p>
100  * <code>ref</code> element will receive same namespace as the input of the
101  * renderer it references. All child <code>child-renderer</code> elements will
102  * have same namespace as their parent <code>ref</code>. This will allow GENE
103  * to provide correct namespace paths.
104  * </p>
105  * <p>
106  * Following this algorithm (when GENE resolves all {@link SplittedDocHolder#PI_GENEREF_TARGET gene-ref PI}
107  * elements) we receive this document in the {@link Coordinator#finishExport()}
108  * method: root element is <code>keyref</code> identifying root renderer. Each
109  * <code>keyref</code> contains zero or more <code>child-renderer</code>
110  * elements, containing ids of children renderers. Each
111  * <code>child-renderer</code> contains one <code>keyref</code> element,
112  * identifying this renderer, etc.
113  * </p>
114  * @author Martin Vysny
115  */
116 public final class GeneDataProvider {
117 	/***
118 	 * Localname (and qname, because of <code>null</code> namespace) of the
119 	 * <code>keyref</code> element.
120 	 */
121 	private final static String KEYREF_ELEMENT_NAME = "keyref"; //$NON-NLS-1$
122 	/***
123 	 * Localname (and qname, because of <code>null</code> namespace) of the
124 	 * <code>child-renderer</code> element.
125 	 */
126 	private final static String CHILD_RENDERER_ELEMENT_NAME = "child-renderer"; //$NON-NLS-1$
127 	/***
128 	 * Localname (and qname, because of <code>null</code> namespace) of the
129 	 * <code>id</code> attribute.
130 	 */
131 	private final static String ID_ATTRIBUTE_NAME = "id"; //$NON-NLS-1$
132 	/***
133 	 * Constructor.
134 	 * @param selector the renderer selector.
135 	 * @param xmlAccess the transformed document.
136 	 * @param edr the data receiver.
137 	 */
138 	public GeneDataProvider(final RendererSelector selector,
139 			final XMLAccess xmlAccess, final IEditorDataReceiver edr) {
140 		super();
141 		this.selector = new RendererSelector(selector);
142 		this.coordinator = new Coordinator();
143 		this.coordinator.edr = edr;
144 		edr.initReceiver();
145 		this.context = new RendererContext(this.coordinator.geneIdMapping,
146 				this.coordinator.rendererKeys, xmlAccess);
147 		// construct accepted namespaces map.
148 		final Map<String, EnumSet<SourceEnum>> accepts = selector
149 				.getRenderable();
150 		// create the coordinator info instance.
151 		this.coordinatorInfo = new CoordinatorInfo(COORDINATOR_ID, null,
152 				"The GENE output to editor framework data redirector", //$NON-NLS-1$
153 				"application/X-gene-direct-rendering", null, accepts); //$NON-NLS-1$
154 	}
155 	/***
156 	 * The renderer selector instance.
157 	 */
158 	private final RendererSelector selector;
159 	/***
160 	 * Returns the coordinator instance - this object receives GENE output.
161 	 * @return GENE's coordinator instance.
162 	 */
163 	public ICoordinator getCoordinator() {
164 		return this.coordinator;
165 	}
166 	/***
167 	 * The coordinator id.
168 	 */
169 	private final static String COORDINATOR_ID = GeneDataProvider.class
170 			.getName()
171 			+ GeneDataProvider.class.hashCode();
172 	/***
173 	 * <p>
174 	 * GENE's coordinator instance, captures GENE output and builds a tree of
175 	 * renderers.
176 	 * </p>
177 	 * <p>
178 	 * The following properties are valid when the GENE data receiving is
179 	 * complete:
180 	 * </p>
181 	 * <ul>
182 	 * <li>{@link #rendererKeys} - maps coordinator 'pipe' key to the renderer,
183 	 * processing the pipe output.</li>
184 	 * <li>{@link #rootKey} - defines root nametree and its renderer.</li>
185 	 * <li>{@link #geneIdMapping} - useful for {@link RendererContext} to
186 	 * resolve GENE-specific IDs (passed in form of <code>emp:mark</code>
187 	 * elements in Source to the renderer) into the renderer instances.</li>
188 	 * <li>{@link #nametreeHierarchy} - defines the nametree hierarchy. Useful
189 	 * for {@link EditorSite} when creating IEditor instances.</li>
190 	 * <li>{@link #newKeys} and {@link #deletedKeys} - shows new or deleted
191 	 * nametrees.</li>
192 	 * </ul>
193 	 * @author Martin Vysny
194 	 */
195 	private final class Coordinator implements ICoordinator {
196 		/***
197 		 * The data receiver.
198 		 */
199 		private IEditorDataReceiver edr;
200 		/***
201 		 * Constructor.
202 		 */
203 		public Coordinator() {
204 			super();
205 		}
206 		/*
207 		 * (non-Javadoc)
208 		 * @see sk.uniba.euromath.api.export.ICoordinator#finishExport()
209 		 */
210 		public void finishExport() throws ExportException, IOException {
211 			// process the rendererTree
212 			final DocumentFragment frag = (DocumentFragment) this.rendererTree
213 					.getNode();
214 			// the tree must contain a sole keyref element
215 			final Element rootKeyRef = (Element) frag.getFirstChild();
216 			assert rootKeyRef != null;
217 			this.geneIdMapping.clear();
218 			this.rootKey = processHierarchy(rootKeyRef);
219 			// we have the hierarchy. the DOM tree is no longer needed, let gc
220 			// get rid of it.
221 			this.rendererTree = null;
222 			this.keyMap.clear();
223 			// we have to remove all deleted keys from our structures.
224 			this.rendererKeys.keySet().removeAll(this.deletedKeys);
225 			// now, all structures are filled correctly. Initialize all
226 			// renderers.
227 			// note that the context is already initialized and configured
228 			// properly - it works on live maps.
229 			// we have to carefully initialize renderers - we must not try to
230 			// initialize renderer that have not-yet-initialized descendant
231 			// renderers.
232 			try {
233 				initializeRenderer(this.rootKey);
234 			} catch (EditorException ex) {
235 				throw new ExportException("Failed to initialize renderers", ex); //$NON-NLS-1$
236 			}
237 			// inform EditorSite that changes were made.
238 			try {
239 				this.edr.dataChanged(this.rendererKeys, this.rootKey,
240 						this.nametreeHierarchy, this.geneIdMapping,
241 						this.newKeys, this.deletedKeys);
242 			} catch (EditorException ex) {
243 				throw new ExportException(ex);
244 			}
245 		}
246 		/***
247 		 * Processes hierarchy of <code>keyref</code> and
248 		 * <code>child-renderer</code> elements.
249 		 * @param keyRef the <code>keyref</code> element.
250 		 * @return the coordinator input key, represented by the
251 		 * <code>keyref</code> element.
252 		 */
253 		private CoordinatorInputKey processHierarchy(final Element keyRef) {
254 			assert keyRef.getLocalName().equals(KEYREF_ELEMENT_NAME);
255 			final String id = keyRef.getAttributeNS(null, ID_ATTRIBUTE_NAME);
256 			assert id != null;
257 			final CoordinatorInputKey key = this.keyMap.get(id);
258 			assert key != null;
259 			// process child elements and create the key hierarchy.
260 			Set<CoordinatorInputKey> children = this.nametreeHierarchy.get(key);
261 			if (children == null) {
262 				children = new HashSet<CoordinatorInputKey>();
263 				this.nametreeHierarchy.put(key, children);
264 			}
265 			for (Element childRenderer = (Element) keyRef.getFirstChild(); childRenderer != null; childRenderer = (Element) childRenderer
266 					.getNextSibling()) {
267 				assert childRenderer.getLocalName().equals(
268 						CHILD_RENDERER_ELEMENT_NAME);
269 				final String geneId = childRenderer.getAttributeNS(null,
270 						ID_ATTRIBUTE_NAME);
271 				assert geneId != null;
272 				// it must have exactly one keyref element.
273 				final Element childKeyRef = (Element) childRenderer
274 						.getFirstChild();
275 				assert childKeyRef.getNextSibling() == null;
276 				final CoordinatorInputKey childKey = processHierarchy(childKeyRef);
277 				// register this mapping
278 				this.geneIdMapping.put(geneId, childKey);
279 				children.add(childKey);
280 			}
281 			return key;
282 		}
283 		/***
284 		 * Initializes renderer with given key. Children renderers are
285 		 * initialized before the renderer itself, to prevent queries for
286 		 * uninitialized renderers.
287 		 * @param key identifies the renderer to be initialized.
288 		 * @throws EditorException
289 		 */
290 		private void initializeRenderer(final CoordinatorInputKey key)
291 				throws EditorException {
292 			// initialize children
293 			Set<CoordinatorInputKey> children = this.nametreeHierarchy.get(key);
294 			if ((children != null) && !children.isEmpty()) {
295 				for (final CoordinatorInputKey child : children) {
296 					initializeRenderer(child);
297 				}
298 			}
299 			// now, initialize the renderer itself.
300 			final RendererSite site = this.rendererKeys.get(key);
301 			site.init();
302 		}
303 		/*
304 		 * (non-Javadoc)
305 		 * @see sk.uniba.euromath.api.export.ICoordinator#getGraphBuilder()
306 		 */
307 		public ExportGraphBuilder getGraphBuilder() {
308 			// Let GENE build the export graph.
309 			return new ExportGraphBuilder();
310 		}
311 		
312 		/*
313 		 * (non-Javadoc)
314 		 * @see sk.uniba.euromath.api.export.ICoordinator#getTransformationGraph()
315 		 */
316 		public TransformGraph getTransformationGraph() {
317 			// we should not get here
318 			throw new AssertionError("Should not be called."); //$NON-NLS-1$
319 		}
320 		/*
321 		 * (non-Javadoc)
322 		 * @see sk.uniba.euromath.api.export.ICoordinator#offerObject(java.lang.String)
323 		 */
324 		public Object offerObject(String namespace) throws ExportException {
325 			namespace = StringUtils.defaultString(namespace);
326 			// call appropriate renderer to query for the object.
327 			final List<RendererSite> renderers = this.rendererNamespaces
328 					.get(namespace);
329 			if ((renderers == null) || renderers.isEmpty()) {
330 				// construct a renderer instance and place it into the temporary
331 				// storage.
332 				try {
333 					final RendererInfo info = GeneDataProvider.this.selector
334 							.getRendererInfo(namespace, EnumSet
335 									.of(ResultEnum.OBJECT));
336 					final IRenderer renderer = info.newRenderer();
337 					this.tempStorage.put(namespace, renderer);
338 					return renderer.offerObject();
339 				} catch (EditorException ex) {
340 					throw new ExportException(ex);
341 				}
342 			}
343 			for (final RendererSite r : renderers) {
344 				if (r.renderer.getInfo().sourceTypes
345 						.contains(SourceEnum.OBJECT))
346 					return r.renderer.offerObject();
347 			}
348 			return null;
349 		}
350 		/*
351 		 * (non-Javadoc)
352 		 * @see sk.uniba.euromath.api.export.ICoordinator#partialResultEvent(org.w3c.dom.DocumentFragment,
353 		 * sk.uniba.euromath.api.export.controller.NamespacePath,
354 		 * javax.xml.transform.Source, java.lang.String,
355 		 * sk.uniba.euromath.api.export.CoordinatorInputKey)
356 		 */
357 		public Source partialResultEvent(final DocumentFragment processed,
358 				final NamespacePath path, final Source source,
359 				final String sourceNamespace, final CoordinatorInputKey key)
360 				throws ExportException, IOException {
361 			// get the instance of renderer that will process given source.
362 			RendererSite renderer = null;
363 			// first check if it isn't in temp storage
364 			IRenderer r = this.tempStorage.get(sourceNamespace);
365 			if ((r != null)
366 					&& r.getInfo().sourceTypes.contains(SourceEnum
367 							.valueOf(source.getClass()))) {
368 				// yeah. move it between regular renderers.
369 				this.tempStorage.remove(sourceNamespace);
370 				renderer = registerRenderer(r, key);
371 				assert renderer.getSource() == null;
372 			}
373 			// if no renderer is found, try to retrieve it from the rendererKeys
374 			// map.
375 			if (renderer == null) {
376 				renderer = this.rendererKeys.get(key);
377 				if (renderer != null) {
378 					// gotcha.
379 					assert renderer.getSource() != null;
380 				}
381 			}
382 			// if no renderer is found, we have to create one.
383 			if (renderer == null) {
384 				final EnumSet<ResultEnum> sourceKind = EnumSet.of(SourceEnum
385 						.valueOf(source.getClass()).getPair());
386 				try {
387 					final RendererInfo info = GeneDataProvider.this.selector
388 							.getRendererInfo(sourceNamespace, sourceKind);
389 					r = info.newRenderer();
390 					renderer = registerRenderer(r, key);
391 					assert renderer.getSource() == null;
392 				} catch (EditorException ex) {
393 					throw new ExportException(ex);
394 				}
395 			}
396 			assert renderer != null;
397 			this.keyMap.put(this.rendererKeys.get(key).id, key);
398 			// if(source instanceof DOMSource){
399 			// ExportHelper.serializeDOM(new
400 			// FileOutputStream("C:/srcfragment.xml"),null,null,processed);
401 			// ExportHelper.serializeDOM(new
402 			// FileOutputStream("C:/src.xml"),null,null,((DOMSource)source).getNode());
403 			// }
404 			renderer.setSource(source);
405 			// ok, renderer is instantiated and registered correctly. return the
406 			// keyref element, with all emp:mark elements as children.
407 			final DOMSource result = new DOMSource();
408 			result.setNode(createHierarchyElement(source, renderer));
409 			// this key is present in current data thus is not deleted.
410 			this.deletedKeys.remove(key);
411 			return result;
412 		}
413 		/***
414 		 * Registers renderer into the <code>rendererKeys</code> and
415 		 * <code>rendererNamespaces</code>.
416 		 * @param renderer renderer to register. Must not be initialized.
417 		 * @param key
418 		 * @return the socket instance.
419 		 */
420 		private RendererSite registerRenderer(final IRenderer renderer,
421 				final CoordinatorInputKey key) {
422 			final RendererSite rs = new RendererSite(renderer, key);
423 			if (this.rendererKeys.put(key, rs) != null)
424 				throw new IllegalArgumentException(
425 						"renderer is already registered under key " + key); //$NON-NLS-1$
426 			List<RendererSite> renderers = this.rendererNamespaces
427 					.get(key.targetNamespace);
428 			if (renderers == null) {
429 				renderers = new ArrayList<RendererSite>();
430 				this.rendererNamespaces.put(key.targetNamespace, renderers);
431 			}
432 			renderers.add(rs);
433 			this.newKeys.add(key);
434 			return rs;
435 		}
436 		/***
437 		 * Creates the <code>keyref</code> element, containing all
438 		 * <code>emp:mark</code> elements.
439 		 * @param source the data source. In case of Object source, no
440 		 * <code>emp:mark</code> are generated. This probably changes later,
441 		 * but it requires wide GENE changes.
442 		 * @param renderer the renderer.
443 		 * @return the <code>keyref</code> element.
444 		 */
445 		private Element createHierarchyElement(final Source source,
446 				final RendererSite renderer) {
447 			final Element result = doc.createElementNS(renderer.renderer
448 					.getInfo().sourceNamespace, KEYREF_ELEMENT_NAME);
449 			result.setAttributeNS(null, ID_ATTRIBUTE_NAME, renderer.id);
450 			if (source instanceof ObjectSource) {
451 				// no known way to retrieve gene-ref PIs from an object,
452 				// just quit.
453 				return result;
454 			}
455 			// retrieve all gene-ref PIs, clone them and put them into
456 			// child-renderer elements which are put into the 'result' element.
457 			// no need to have several gene-ref PIs with same id. Remember all
458 			// used ids in this set and filter them out.
459 			final List<ProcessingInstruction> mark = new ArrayList<ProcessingInstruction>();
460 			getDescendantMarkElements(((DOMSource) source).getNode(), mark,
461 					new HashSet<String>());
462 			// enclose all mark elements in the 'child-renderer' element.
463 			for (final ProcessingInstruction m : mark) {
464 				final Element cr = doc.createElementNS(
465 						result.getNamespaceURI(), CHILD_RENDERER_ELEMENT_NAME);
466 				final String empId = SplittedDocHolder.getGeneRef(m);
467 				cr.setAttributeNS(null, ID_ATTRIBUTE_NAME, empId);
468 				final ProcessingInstruction geneRefClone = (ProcessingInstruction) doc.importNode(m, true);
469 				cr.appendChild(geneRefClone);
470 				result.appendChild(cr);
471 			}
472 			return result;
473 		}
474 		/***
475 		 * Retrieves all {@link SplittedDocHolder#PI_GENEREF_TARGET} processing instructions. Filters out multiple
476 		 * instances of {@link SplittedDocHolder#PI_GENEREF_TARGET} with same id.
477 		 * @param node walk through descendants of this node. Returns empty list
478 		 * when <code>null</code>.
479 		 * @param refs place all ref PIs here.
480 		 * @param ids ids of elements present in the result.
481 		 */
482 		private void getDescendantMarkElements(final Node node,
483 				List<ProcessingInstruction> refs, Set<String> ids) {
484 			if (node == null)
485 				return;
486 			final String id = SplittedDocHolder.getGeneRef(node);
487 			if(id!=null){
488 				if (ids.contains(id))
489 					return;
490 				// not yet present in the mark list, append it.
491 				ids.add(id);
492 				refs.add((ProcessingInstruction)node);
493 			}
494 			// walk through its children.
495 			for (Node child = node.getFirstChild(); child != null; child = child
496 					.getNextSibling()) {
497 				getDescendantMarkElements(child, refs, ids);
498 			}
499 		}
500 		/*
501 		 * (non-Javadoc)
502 		 * @see sk.baka.xml.gene.ICoordinator#dispose()
503 		 */
504 		public void dispose() {
505 			// nothing to free
506 		}
507 		/*
508 		 * (non-Javadoc)
509 		 * @see sk.baka.xml.gene.ICoordinator#getConfigurator()
510 		 */
511 		public IConfigurator getConfigurator() {
512 			// nothing to configure
513 			return null;
514 		}
515 		/*
516 		 * (non-Javadoc)
517 		 * @see sk.baka.xml.gene.ICoordinator#getInfo()
518 		 */
519 		public CoordinatorInfo getInfo() {
520 			return coordinatorInfo;
521 		}
522 		/*
523 		 * (non-Javadoc)
524 		 * @see sk.uniba.euromath.api.export.ICoordinator#requestResult(java.lang.String)
525 		 */
526 		public StreamResult requestResult(String fileName) throws IOException {
527 			final StreamResult result = new StreamResult(fileName);
528 			result.setOutputStream(ExportUtils.openStream(new File(fileName)));
529 			return result;
530 		}
531 		/*
532 		 * (non-Javadoc)
533 		 * @see sk.baka.xml.gene.ICoordinator#startExport(java.io.File)
534 		 */
535 		public Result startExport(File systemId) throws ExportException,
536 				IOException {
537 			assert this.keyMap.isEmpty();
538 			// prepare internal structures
539 			this.geneIdMapping.clear();
540 			this.newKeys.clear();
541 			this.deletedKeys.clear();
542 			this.deletedKeys.addAll(this.rendererKeys.keySet());
543 			// ignore all parameters, just return result where a DOM tree will
544 			// be constructed.
545 			this.rendererTree = new DOMResult();
546 			this.rendererTree.setNode(doc.createDocumentFragment());
547 			return this.rendererTree;
548 		}
549 		/***
550 		 * <p>
551 		 * Here a coordinator output is written. This output is intended to show
552 		 * the tree hierarchy of renderers. For further description please see
553 		 * {@link #partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}.
554 		 * </p>
555 		 */
556 		private DOMResult rendererTree = null;
557 		/***
558 		 * <p>
559 		 * Maps input pipe ID to a renderer instance. Filled in
560 		 * {@link #partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}
561 		 * method.
562 		 * </p>
563 		 * <p>
564 		 * {@link #deletedKeys Deleted keys} are automatically removed in
565 		 * {@link #finishExport()}.
566 		 * </p>
567 		 */
568 		public final Map<CoordinatorInputKey, RendererSite> rendererKeys = new HashMap<CoordinatorInputKey, RendererSite>();
569 		/***
570 		 * Maps namespace of the document that the renderer processes, to a
571 		 * renderer instance. Contains same renderer instances as
572 		 * {@link #rendererKeys}.
573 		 */
574 		private final Map<String, List<RendererSite>> rendererNamespaces = new HashMap<String, List<RendererSite>>();
575 		/***
576 		 * <p>
577 		 * Temporary storage of renderer instances. When coordinator is queried
578 		 * for object offer and no renderer for given namespace exists, one is
579 		 * constructed and placed here. When the instance is really needed, it
580 		 * is moved to {@link #rendererKeys} and {@link #rendererNamespaces}
581 		 * maps.
582 		 * </p>
583 		 * <p>
584 		 * Only renderers able to process Object source are placed here.
585 		 * </p>
586 		 */
587 		private final Map<String, IRenderer> tempStorage = new HashMap<String, IRenderer>();
588 		/***
589 		 * Maps {@link RendererSite}'s id to the coordinator input key. Filled
590 		 * in
591 		 * {@link #partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}
592 		 * method, and cleared when the GENE output processing finishes.
593 		 */
594 		private final Map<String, CoordinatorInputKey> keyMap = new HashMap<String, CoordinatorInputKey>();
595 		/***
596 		 * The root key of the hierarchy.
597 		 */
598 		public CoordinatorInputKey rootKey;
599 		/***
600 		 * <p>
601 		 * The nametree (key) hierarchy. Maps key to a set of its children. If
602 		 * key is missing from the map, maps to <code>null</code> value or an
603 		 * empty set then the nametree does not have any children.
604 		 * </p>
605 		 * <p>
606 		 * {@link #deletedKeys Deleted keys} are automatically removed in
607 		 * {@link #finishExport()}.
608 		 * </p>
609 		 */
610 		public final Map<CoordinatorInputKey, Set<CoordinatorInputKey>> nametreeHierarchy = new HashMap<CoordinatorInputKey, Set<CoordinatorInputKey>>();
611 		/***
612 		 * <p>
613 		 * Maps GENE-generated ids to the coordinator input key instance. Used
614 		 * by renderer context to map from GENE id (provided to the renderer) to
615 		 * child renderer instance.
616 		 * </p>
617 		 * <p>
618 		 * Constructed in {@link #finishExport()}.
619 		 * </p>
620 		 */
621 		public final Map<String, CoordinatorInputKey> geneIdMapping = new HashMap<String, CoordinatorInputKey>();
622 		/***
623 		 * <p>
624 		 * Contains set of new keys (keys that were not introduced in previous
625 		 * GENE output processing).
626 		 * </p>
627 		 * <p>
628 		 * Cleared in {@link #startExport(File)}, filled in
629 		 * {@link #partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}.
630 		 * </p>
631 		 */
632 		public final Set<CoordinatorInputKey> newKeys = new HashSet<CoordinatorInputKey>();
633 		/***
634 		 * <p>
635 		 * Contains set of keys that were introduced in previous GENE output
636 		 * processing but they are missing in current data.
637 		 * </p>
638 		 * <p>
639 		 * Initialized in {@link #startExport(File)},
640 		 * processed in
641 		 * {@link #partialResultEvent(DocumentFragment, NamespacePath, Source, String, CoordinatorInputKey)}.
642 		 * </p>
643 		 */
644 		public final Set<CoordinatorInputKey> deletedKeys = new HashSet<CoordinatorInputKey>();
645 		/*
646 		 * (non-Javadoc)
647 		 * @see sk.uniba.euromath.gene.ICoordinator#alterContext(sk.uniba.euromath.gene.IExporter,
648 		 * sk.uniba.euromath.gene.ExportContext,
649 		 * sk.uniba.euromath.gene.controller.NamespacePath)
650 		 */
651 		public void alterContext(IExporter exporter, ExportContext context,
652 				NamespacePath path) {
653 			// each exporter is fully separated, thus each is the root one.
654 			context.isRoot = true;
655 			context.parentNamespace = null;
656 		}
657 	}
658 	/***
659 	 * Represents the renderer with its environment.
660 	 * @author Martin Vysny
661 	 */
662 	public final class RendererSite {
663 		/***
664 		 * Constructor.
665 		 * @param renderer the renderer instance.
666 		 * @param input the input path identifier.
667 		 */
668 		public RendererSite(final IRenderer renderer,
669 				final CoordinatorInputKey input) {
670 			super();
671 			if (renderer == null)
672 				throw new IllegalArgumentException("null renderer"); //$NON-NLS-1$
673 			this.renderer = renderer;
674 			this.preprocessor = renderer.createPreprocessor();
675 			if (this.preprocessor != null) {
676 				this.preprocessor.init(input);
677 			}
678 		}
679 		/***
680 		 * The renderer instance.
681 		 */
682 		public final IRenderer renderer;
683 		/***
684 		 * Input preprocessor instance.
685 		 */
686 		private final IInputPreprocessor preprocessor;
687 		/***
688 		 * Source data for the renderer. May be DOM or Object only. Do not set
689 		 * directly, use {@link #setSource(Source)} instead.
690 		 */
691 		private IChangeCollector source;
692 		/***
693 		 * Sets given source to the renderer.
694 		 * @param source the source to set.
695 		 */
696 		public void setSource(final Source source) {
697 			if (this.source == null) {
698 				if (source instanceof DOMSource) {
699 					this.source = new DOMChangeCollector();
700 				} else if (source instanceof ObjectSource) {
701 					this.source = new ObjectChangeCollector();
702 				} else
703 					throw new IllegalArgumentException(
704 							"Illegal source type: " + source.getClass().getName());//$NON-NLS-1$
705 				this.source.init(source);
706 			} else {
707 				this.source.reinit(source);
708 			}
709 			if (this.preprocessor == null) {
710 				return;
711 			}
712 			this.source = this.preprocessor.process(this.source);
713 			if (this.source.getSource() == null)
714 				throw new IllegalStateException("Preprocessor " //$NON-NLS-1$
715 						+ this.preprocessor.getClass().getName()
716 						+ " failed to initialize result change collector."); //$NON-NLS-1$
717 		}
718 		/***
719 		 * Returns the source object.
720 		 * @return the source.
721 		 */
722 		public IChangeCollector getSource() {
723 			return this.source;
724 		}
725 		/***
726 		 * An unique integer id.
727 		 */
728 		public final String id = String
729 				.valueOf(++GeneDataProvider.this.rendererSiteId);
730 		/***
731 		 * <code>false</code> if the renderer is not new and needs to
732 		 * reinitialize instead of initialization.
733 		 */
734 		private boolean isNew = true;
735 		/***
736 		 * Checks if this renderer is new or being reused.
737 		 * @return <code>false</code> if the renderer is not new and needs to
738 		 * reinitialize instead of initialization.
739 		 */
740 		public boolean isNew() {
741 			return this.isNew;
742 		}
743 		/***
744 		 * Initializes (or reinitializes) the renderer.
745 		 * @throws EditorException if initialization fails.
746 		 */
747 		public void init() throws EditorException {
748 			if (this.source == null)
749 				throw new IllegalStateException("source is null"); //$NON-NLS-1$
750 			if (this.isNew) {
751 				this.isNew = false;
752 				this.renderer.init(this.source.getSource(),
753 						GeneDataProvider.this.context);
754 				return;
755 			}
756 			// we have to reinit the renderer
757 			this.renderer.reinit(this.source);
758 		}
759 	}
760 	/***
761 	 * Unique id for the renderer site instance.
762 	 */
763 	private int rendererSiteId = 0;
764 	/***
765 	 * The coordinator instance.
766 	 */
767 	private final Coordinator coordinator;
768 	/***
769 	 * The coordinator info.
770 	 */
771 	private final CoordinatorInfo coordinatorInfo;
772 	/***
773 	 * Returns the coordinator information instance for the GENE coordinator.
774 	 * @return coordinator info for this coordinator.
775 	 */
776 	public CoordinatorInfo getCoordinatorInfo() {
777 		return this.coordinatorInfo;
778 	}
779 	/***
780 	 * The context object for renderers.
781 	 */
782 	private final RendererContext context;
783 	/***
784 	 * The document instance, owning all nodes created by this class.
785 	 */
786 	private static final Document doc = TransformUtils.builder.newDocument();
787 }