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