1 /*
2 * Copyright (c) 2003
3 * Information Desire GmbH
4 * All rights reserved.
5 */
6 package com.infodesire.infobit.external.impl;
7
8 import com.infodesire.infobit.InfobitManager;
9 import com.infodesire.infobit.InfobitException;
10 import com.infodesire.infobit.InfobitSecurityException;
11
12 import com.infodesire.infobit.data.Acl;
13 import com.infodesire.infobit.data.Infobit;
14
15 import com.infodesire.infobit.external.ImportMessage;
16 import com.infodesire.infobit.external.InfobitImporter;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 import java.io.Reader;
22
23 import java.util.ArrayList;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.SortedSet;
32 import java.util.TreeSet;
33
34 /***
35 * This class provides a straight-forward implementation to import infobits from
36 * their XML representation, as defined by {@link InfobitImporter}.
37 *
38 * @author peter2
39 * @created 1. September 2003
40 * @version $Revision: 1.1 $
41 */
42 public class ImporterImpl
43 implements InfobitImporter, InfobitDictionary, ImportDiagnostics {
44
45 private final static Log _log = LogFactory.getLog(ImporterImpl.class);
46
47 /***
48 * The ACL used to create infobits and content.
49 */
50 private Acl _acl;
51
52 /***
53 * The collaborating manager
54 */
55 private InfobitManager _manager;
56
57 /***
58 * System ID of the XML document to import
59 */
60 private String _path;
61
62 /***
63 * List of diagnostics messages applicable to the currently processed {@link
64 * importInfobits import operation}. List of {@link ImportMessage}.
65 */
66 private List _diagnostics;
67
68 /***
69 * List of all imported infobits after completion, in order of import. List
70 * of {@link Infobit}.
71 */
72 private List _infobits = new ArrayList();
73
74 /***
75 * Lexicon of all infobits involved in the import. Maps <code>String</code>
76 * infobit names to {@link InfobitNode}s representing infobits.
77 */
78 private Map _infobitRegister = new HashMap();
79
80 /***
81 * Collects all buffered infobits and version nodes, ordered by definition
82 * in the XML import document. Set of {@link BufferNode}.
83 */
84 private List _bufferNodes;
85
86 /***
87 * Next timestamp value used for {@link #colorGraph depth-first search}
88 */
89 private int _nextTime = 1;
90
91 /***
92 * Buffer nodes in linear dependency order, with the least dependent nodes
93 * first. Set of {@link BufferNode}.
94 */
95 private SortedSet _orderedNodes =
96 new TreeSet(
97 new Comparator() {
98 public int compare(Object a, Object b) {
99 BufferNode x = (BufferNode) a;
100 BufferNode y = (BufferNode) b;
101
102 return x.getCompleteTime() - y.getCompleteTime();
103 }
104
105
106 public boolean equals(Object obj) {
107 return false;
108 }
109 });
110
111 /***
112 * For each dependency cycle, this collection contains an edge closing the
113 * cycle (backward edge). Set of {@link BufferEdges}.
114 */
115 private Set _backwardEdges = new HashSet();
116
117 /***
118 * Maps all processed infobit nodes to their resolved infobits. Map of
119 * {@link InfobitNode} to {@link Infobit}.
120 */
121 private Map _resolvedInfobits = new HashMap();
122
123
124 /***
125 * Creates an instance ready to set the manager and to {@link #init
126 * initialize}.
127 */
128 public ImporterImpl() {
129 // STUB
130 }
131
132
133 /***
134 * Gets the manager to work with.
135 *
136 * @return The manager to access infobits
137 */
138 public InfobitManager getManager() {
139 return _manager;
140 }
141
142
143 /***
144 * Defines the manager to work with.
145 *
146 * @param manager The manager to access infobits
147 */
148 public void setManager(InfobitManager manager) {
149 _manager = manager;
150 }
151
152
153 /***
154 * Defines the ACL used to create imported infobits and content
155 *
156 * @param acl The new Acl value
157 */
158 public void setAcl(Acl acl) {
159 _acl = acl;
160 }
161
162
163 /***
164 * Recovers infobits from an XML representation as created by {@link
165 * InfobitExporter#export InfobitExporter.export}, as specified for {@link
166 * InfobitImporter}. <p>
167 *
168 * <em<BUGS</em> : This implementation returns infobits not imported but
169 * referenced from imported ones.
170 *
171 * @param input Source from which to obtain the XML
172 * document representing the infobits to import
173 * @param path System location of <code>input</code>
174 * , used in diagnostic messages
175 * @param diagnostics A list to be filled with {@link
176 * ImportMessage} instances providing diagnostic messages about errors
177 * encountered during the import.
178 * @return A collection of the imported
179 * infobits.
180 * @exception InfobitException Generic processing error
181 * @exception InfobitSecurityException Description of Exception
182 */
183 public List importInfobits(Reader input, String path, List diagnostics)
184 throws InfobitException, InfobitSecurityException {
185
186 _path = path;
187 _diagnostics = diagnostics;
188 _diagnostics.clear();
189 _infobits.clear();
190 _infobitRegister.clear();
191
192 buildBuffer(input);
193 orderNodes();
194 checkNodes();
195
196 if (_diagnostics.size() == 0) {
197 persistNodes();
198 extractInfobits();
199 }
200
201 return _infobits;
202 }
203
204
205 /***
206 * Prepares the instance to be used. No action for this implementation.
207 *
208 * @exception InfobitException Description of Exception
209 */
210 public void init() throws InfobitException {
211 }
212
213
214 ////////////////////////////////////////////////////////////////////////
215 // InfobitDictionary Implementation
216 ////////////////////////////////////////////////////////////////////////
217
218 /***
219 * DOCUMENT METHOD
220 *
221 * @param node Description of Parameter
222 */
223 public void register(InfobitNode node) {
224 _infobitRegister.put(node.getName(), node);
225 node.setDangling(false);
226 }
227
228
229 /***
230 * DOCUMENT METHOD
231 *
232 * @param name Description of Parameter
233 * @return Description of the Returned Value
234 */
235 public InfobitNode lookup(String name) {
236 InfobitNode node = (InfobitNode) _infobitRegister.get(name);
237
238 if (node == null) {
239 node = new InfobitNode();
240 node.setName(name);
241 node.setDangling(true);
242 _infobitRegister.put(name, node);
243 }
244
245 return node;
246 }
247
248
249 ////////////////////////////////////////////////////////////////////////
250 // ImportDiagnostics Implementation
251 ////////////////////////////////////////////////////////////////////////
252
253 /***
254 * DOCUMENT METHOD
255 *
256 * @param message Description of Parameter
257 */
258 public void record(ImportMessage message) {
259 _diagnostics.add(message);
260 _log.trace("Import error: " + message.getMessage());
261 }
262
263
264 ////////////////////////////////////////////////////////////////////////
265 // Implementation private
266 ////////////////////////////////////////////////////////////////////////
267
268 /***
269 * Converts an XML document representing exported infobits into the buffer
270 * graph. The graph is not coloured, and references are not resolved. Sets
271 * {@link #_bufferNodes}.
272 *
273 * @param input Description of Parameter
274 * @exception InfobitException Description of Exception
275 * @exception InfobitSecurityException Description of Exception
276 */
277 private void buildBuffer(Reader input)
278 throws InfobitException, InfobitSecurityException {
279
280 InfobitParser parser = new InfobitParser(this, this);
281 parser.setSystemID(_path);
282 parser.buildTree(input);
283 _bufferNodes = parser.getBufferNodes();
284 }
285
286
287 /***
288 * Checks the dependency graph fro acyclicity and creates a linear order.
289 */
290 private void orderNodes() {
291 for (Iterator i = _bufferNodes.iterator(); i.hasNext(); ) {
292 BufferNode node = (BufferNode) i.next();
293
294 if (node.getColor() == BufferNode.WHITE) {
295 colorGraph(node);
296 }
297 }
298
299 reportCycles();
300 }
301
302
303 /***
304 * Checks the linearly ordered items to be imported on consistency.
305 *
306 * @exception InfobitException Description of Exception
307 */
308 private void checkNodes() throws InfobitException {
309 for (Iterator i = _bufferNodes.iterator(); i.hasNext(); ) {
310 BufferNode node = (BufferNode) i.next();
311
312 if (node instanceof InfobitNode) {
313 check((InfobitNode) node);
314 }
315 else if (node instanceof ActualVersionNode) {
316 check((ActualVersionNode) node);
317 }
318 }
319 }
320
321
322 /***
323 * Successively resolves buffer dependencies and persists the items to be
324 * imported. Stores imported infobits to {@link _resolvedInfobits}.
325 *
326 * @exception InfobitException Description of Exception
327 * @exception InfobitSecurityException Description of Exception
328 */
329 private void persistNodes()
330 throws InfobitException, InfobitSecurityException {
331
332 _resolvedInfobits.clear();
333 Map persistedNodes = new HashMap();
334
335 for (Iterator i = _orderedNodes.iterator(); i.hasNext(); ) {
336 BufferNode node = (BufferNode) i.next();
337
338 node.setManager(_manager);
339 node.setAcl(_acl);
340
341 for (Iterator j = node.getDependencies().iterator();
342 j.hasNext(); ) {
343
344 BufferNode dep = (BufferNode) j.next();
345 Object p = persistedNodes.get(dep);
346
347 // NOTE: due to the linear order of dependencies,
348 // p != null is an invariant
349 if (p == null) {
350 String m = "Failed assertion: unresolved dependency of " +
351 node.getClass().getName() +
352 " to " + dep.getClass().getName();
353 _log.fatal(m);
354 throw new RuntimeException(m);
355 }
356
357 node.resolveDependency(dep, p);
358 }
359
360 Object persisted = node.persistBuffer();
361 persistedNodes.put(node, persisted);
362
363 if (node instanceof InfobitNode) {
364 InfobitNode n = (InfobitNode) node;
365
366 // TEST
367 // if (!n.isPredefined()) {
368 if (true || !n.isPredefined()) {
369 _resolvedInfobits.put(node, persisted);
370 }
371 }
372 }
373 }
374
375
376 /***
377 * Sets up the final ordered list of imported infobits.
378 */
379 private void extractInfobits() {
380 _infobits.clear();
381
382 for (Iterator i = _bufferNodes.iterator(); i.hasNext(); ) {
383 BufferNode bn = (BufferNode) i.next();
384 Infobit ib = (Infobit) _resolvedInfobits.get(bn);
385
386 if (ib != null) {
387 _infobits.add(ib);
388 }
389 }
390 }
391
392
393 /***
394 * Performs depth-first search on the nodes reachable from the specfied
395 * node. Colors all reached nodes and edges. Stores nodes to {@link
396 * _orderedNodes}.
397 *
398 * @param node Description of Parameter
399 */
400 private void colorGraph(BufferNode node) {
401 node.setColor(BufferNode.GRAY);
402 node.setDiscoverTime(_nextTime++);
403
404 for (Iterator i = node.getEdges().iterator(); i.hasNext(); ) {
405 BufferEdge edge = (BufferEdge) i.next();
406 BufferNode end = edge.getDependency();
407
408 switch (end.getColor()) {
409 case BufferNode.WHITE:
410 colorGraph(end);
411 // edge.setKind(BufferEdge.TREE);
412 break;
413 case BufferNode.GRAY:
414 // edge.setKind(BufferEdge.BACKWARD);
415 _backwardEdges.add(edge);
416 break;
417 case BufferNode.BLACK:
418 // edge.setKind(BufferEdge.FORWARD_OR_CROSS);
419 break;
420 }
421 }
422
423 node.setColor(BufferNode.BLACK);
424 node.setCompleteTime(_nextTime++);
425 _orderedNodes.add(node);
426 }
427
428
429 /***
430 * Creates error messages on cyclic dependencies from {@link
431 * _backwardNodes}.
432 */
433 private void reportCycles() {
434 for (Iterator i = _backwardEdges.iterator(); i.hasNext(); ) {
435 BufferEdge edge = (BufferEdge) i.next();
436 BufferNode node = edge.getDependency();
437 error(node, "This node infers a cyclic dependency");
438 }
439
440 // Currently, no cycles are possible
441 // if (_backwardEdges.size() > 0) {
442 // _log.fatal("Failed assertion: cyclic dependency");
443 // throw new RuntimeException("Failed assertion: cyclic dependency");
444 // }
445 }
446
447
448 /***
449 * Checks consistency of an infobit to create. Adds error messges.
450 *
451 * @param node Description of Parameter
452 * @exception InfobitException Description of Exception
453 */
454 private void check(InfobitNode node)
455 throws InfobitException {
456
457 String name = node.getName();
458 boolean predefined = _manager.hasInfobit(name);
459 node.setPredefined(predefined);
460
461 if (node.isDangling()) {
462 node.setDangling(predefined);
463 }
464 else if (predefined) {
465 error(node,
466 "The node '" + name + "' to import already exists" +
467 " in the system.");
468 }
469
470 if (node.isDangling()) {
471 error(node,
472 "The referenced Node '" + name + "' is neither" +
473 " imported nor pre-existent in the system");
474
475 for (Iterator i = node.getDependents().iterator(); i.hasNext(); ) {
476 BufferEdge edge = (BufferEdge) i.next();
477 error(node, "Node '" + name + "' referenced from here");
478 }
479 }
480
481 }
482
483
484 /***
485 * Checks the assignement of an actual version for consistency. Emits error
486 * messages if appropriate.
487 *
488 * @param node Description of Parameter
489 */
490 private void check(ActualVersionNode node) {
491 if (node.getActualVersionNode() == null) {
492 error(node,
493 "Node '" +
494 node.getInfobitNode().getName() + "' has" +
495 "an undefined version set as its actual version.");
496 }
497 }
498
499
500 /***
501 * Records an error condition for the specified buffer node. Additionally,
502 * logs the error.
503 *
504 * @param node The node to which <code>message</code> applies
505 * @param msg Description of Parameter
506 */
507 private void error(BufferNode node, String msg) {
508 String path = node.locateSystemID();
509 int line = node.locateLine();
510
511 StringBuffer b = new StringBuffer();
512 if (path != null) {
513 b.append(path);
514 b.append(":");
515 }
516
517 if (b.length() > 0 && line > 0) {
518 b.append(":");
519 b.append(line);
520 }
521
522 b.append(" Error in import: ");
523 b.append(msg);
524
525 _log.error(b.toString());
526 _diagnostics.add(new ImportMessage(path, line, 0, msg));
527 }
528
529 }
530
This page was automatically generated by Maven