1 package net.sf.tomp.xml.type;
2
3 import net.sf.tomp.xtcl.filter.XTFilterFactory;
4 import net.sf.tomp.xtcl.result.HasResult;
5
6 import org.apache.commons.logging.Log;
7 import org.apache.commons.logging.LogFactory;
8 import org.xml.sax.Attributes;
9 import org.xml.sax.SAXException;
10 import org.xml.sax.XMLFilter;
11 import org.xml.sax.helpers.DefaultHandler;
12
13 import javax.xml.transform.Result;
14 import javax.xml.transform.TransformerConfigurationException;
15 import javax.xml.transform.sax.SAXResult;
16
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.SortedSet;
23 import java.util.TreeSet;
24
25 /***
26 * DOCUMENT ME!
27 *
28 * @author $author$
29 * @version $Revision$
30 */
31 public class TypeDatabaseImpl extends DefaultHandler
32 implements TypeDatabase, TypeTransformationFactory, HasResult {
33
34 private Log log = LogFactory.getLog(TypeDatabaseImpl.class);
35
36 /*** DOCUMENT ME! */
37 protected XTFilterFactory xff;
38
39 /***
40 * parent type database. If it is set, then all queries not answered here
41 * are forwarded to parent TD
42 */
43 protected TypeDatabase parentDatabase;
44
45 /*** DOCUMENT ME! */
46 protected Type currentType;
47
48 /*** DOCUMENT ME! */
49 protected Type currentSrcType;
50
51 /*** DOCUMENT ME! */
52 protected Type currentTargetType;
53
54 /*** DOCUMENT ME! */
55 protected String currentSource;
56
57 /*** DOCUMENT ME! */
58 protected String currentTarget;
59
60 /*** DOCUMENT ME! */
61 protected TypeTransformation currentTransformation;
62
63 /*** DOCUMENT ME! */
64 protected MultiFilterTypeTransformation currentMultiTransformation;
65
66 /*** DOCUMENT ME! */
67 protected Variant currentVariant;
68
69 /*** DOCUMENT ME! */
70 protected Map identification2type = new HashMap();
71
72 /*** DOCUMENT ME! */
73 protected Map root2type = new HashMap();
74
75 /*** DOCUMENT ME! */
76 protected Map doctype2type = new HashMap();
77
78
79
80
81
82
83 /*** DOCUMENT ME! */
84 protected Transformations transformations = new Transformations();
85
86 /***
87 * DOCUMENT ME!
88 *
89 * @param x DOCUMENT ME!
90 */
91 public void setXTFilterFactory(XTFilterFactory x) {
92 xff = x;
93 }
94
95 /***
96 * DOCUMENT ME!
97 *
98 * @return DOCUMENT ME!
99 */
100 public TypeDatabase getParent() {
101 return parentDatabase;
102 }
103
104 /***
105 * DOCUMENT ME!
106 *
107 * @param td DOCUMENT ME!
108 */
109 public void setParent(TypeDatabase td) {
110 parentDatabase = td;
111 }
112
113 /***
114 * DOCUMENT ME!
115 *
116 * @return DOCUMENT ME!
117 */
118 public Result getResult() {
119 return new SAXResult(this);
120 }
121
122 /***
123 * DOCUMENT ME!
124 *
125 * @return DOCUMENT ME!
126 */
127 public int getTypesCount() {
128 return identification2type.entrySet().size();
129 }
130
131 /***
132 * DOCUMENT ME!
133 *
134 * @return DOCUMENT ME!
135 */
136 public Transformations getTransformations() {
137 return transformations;
138 }
139
140 /***
141 * implementation of transformation info storage
142 *
143 * @param source DOCUMENT ME!
144 * @param target DOCUMENT ME!
145 * @param variant DOCUMENT ME!
146 * @param tt DOCUMENT ME!
147 */
148
149
150
151
152
153
154 public void addTransformation(Type source, Type target, Variant variant,
155 TypeTransformation tt) {
156
157
158
159
160
161
162 TransformationsFromType tft = transformations
163 .getTransformationsFrom(source);
164
165 if (tft == null) {
166 tft = new TransformationsFromType(source);
167 transformations.putTransformationsFrom(tft);
168 }
169
170 tft.putTransformationTo(target, variant, tt);
171 }
172
173 /***
174 * DOCUMENT ME!
175 *
176 * @param source DOCUMENT ME!
177 * @param target DOCUMENT ME!
178 * @param variant DOCUMENT ME!
179 * @return DOCUMENT ME!
180 */
181 public TypeTransformation getTransformation(Type source, Type target,
182 Variant variant) {
183 if (source == target) {
184 return null;
185 }
186
187
188
189
190
191
192
193
194
195
196 TransformationsFromType tft = transformations
197 .getTransformationsFrom(source);
198
199 if (tft == null) {
200 if (parentDatabase == null) {
201 return null;
202 } else {
203 return parentDatabase
204 .getTransformation(source, target, variant);
205 }
206 }
207
208 TypeTransformation tt = tft.getTransformationTo(target, variant);
209
210 if (tt == null) {
211 log.debug("+++ SEARCHING transformationSequence to=" + target
212 + " variant=" + variant);
213
214 List transformationSequence = getTransformationSequence(source,
215 target, variant);
216
217 log.debug("+++ FOUND transformationSequence="
218 + transformationSequence);
219
220 if ((transformationSequence == null)
221 || (transformationSequence.size() == 0)) {
222 return null;
223 }
224
225 TypeTransformation mt = new MultiFilterTypeTransformation(
226 transformationSequence);
227
228
229 tft.putTransformationTo(target, variant, mt);
230
231 return mt;
232 }
233
234 return tft.getTransformationTo(target, variant);
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 /***
255 * implementation of type info storage
256 *
257 * @param ns DOCUMENT ME!
258 * @param localName DOCUMENT ME!
259 * @return DOCUMENT ME!
260 */
261 public Type getTypeForRootElement(String ns, String localName) {
262 Object t = root2type.get("{" + ns + "}" + localName);
263
264 if (t == null) {
265 t = root2type.get("{" + ns + "}*");
266 }
267
268 if (t == null) {
269 t = root2type.get("{*}" + localName);
270 }
271
272 return (Type) t;
273 }
274
275 /***
276 * DOCUMENT ME!
277 *
278 * @param name DOCUMENT ME!
279 * @param dtPublic DOCUMENT ME!
280 * @param dtSystem DOCUMENT ME!
281 * @return DOCUMENT ME!
282 */
283 public Type getTypeForDoctype(String name, String dtPublic, String dtSystem) {
284 return (Type) doctype2type.get(dtPublic);
285 }
286
287 /***
288 * DOCUMENT ME!
289 *
290 * @param id DOCUMENT ME!
291 * @return DOCUMENT ME!
292 */
293 public Type getType(String id) {
294 return (Type) identification2type.get(id);
295 }
296
297 /***
298 * DOCUMENT ME!
299 *
300 * @param fn DOCUMENT ME!
301 * @return DOCUMENT ME!
302 * @throws ClassNotFoundException DOCUMENT ME!
303 * @throws InstantiationException DOCUMENT ME!
304 * @throws IllegalAccessException DOCUMENT ME!
305 */
306 public TypeTransformation newTransformationForFilterClassName(String fn)
307 throws ClassNotFoundException, InstantiationException,
308 IllegalAccessException {
309 return new FilterTypeTransformation(fn);
310 }
311
312 /***
313 * DOCUMENT ME!
314 *
315 * @param ssi DOCUMENT ME!
316 * @return DOCUMENT ME!
317 * @throws TransformerConfigurationException DOCUMENT ME!
318 */
319 public TypeTransformation newTransformationForStyleSystemId(String ssi)
320 throws TransformerConfigurationException {
321
322
323 return new StyleTypeTransformation(xff, ssi);
324 }
325
326 /***
327 * DOCUMENT ME!
328 *
329 * @param ssi DOCUMENT ME!
330 * @return DOCUMENT ME!
331 * @throws TransformerConfigurationException DOCUMENT ME!
332 */
333 public TypeTransformation newTransformationForSTXStyleSystemId(String ssi)
334 throws TransformerConfigurationException {
335
336
337 return new StyleTypeTransformation(xff, ssi);
338 }
339
340
341
342 /***
343 * Filter a start document event.
344 *
345 * @param uri DOCUMENT ME!
346 * @param localName DOCUMENT ME!
347 * @param qName DOCUMENT ME!
348 * @param atts DOCUMENT ME!
349 * @exception SAXException The client may throw an exception during
350 * processing.
351 */
352
353
354
355
356
357
358 /***
359 * Filter an end document event.
360 *
361 * @param uri DOCUMENT ME!
362 * @param localName DOCUMENT ME!
363 * @param qName DOCUMENT ME!
364 * @param atts DOCUMENT ME!
365 * @exception SAXException The client may throw an exception during
366 * processing.
367 */
368
369
370
371
372
373
374
375
376
377
378
379 /***
380 * Filter a start element event.
381 *
382 * @param uri The element's Namespace URI, or the empty string.
383 * @param localName The element's local name, or the empty string.
384 * @param qName The element's qualified (prefixed) name, or the empty
385 * string.
386 * @param atts The element's attributes.
387 * @exception SAXException The client may throw an exception during
388 * processing.
389 */
390 public void startElement(String uri, String localName, String qName,
391 Attributes atts) throws SAXException {
392 XMLFilter filter = null;
393
394 if ("type".equals(qName)) {
395 for (int i = 0; i < atts.getLength(); i++) {
396 String aName = atts.getQName(i);
397
398 if (aName.startsWith("by-")) {
399 String typeType = aName.substring("by-".length());
400 String identification = atts.getValue(i);
401
402 currentType = new TypeImpl(typeType, identification);
403 identification2type.put(typeType + ":" + identification,
404 currentType);
405
406 break;
407 }
408 }
409 } else if ("subtype-of".equals(qName) || "parent-is".equals(qName)) {
410 String typeIdentification = null;
411
412 for (int i = 0; i < atts.getLength(); i++) {
413 String aName = atts.getQName(i);
414
415 if (aName.startsWith("by-")) {
416 String typeType = aName.substring("by-".length());
417 String identification = atts.getValue(i);
418
419 typeIdentification = typeType + ":" + identification;
420
421 break;
422 }
423 }
424
425 if (typeIdentification != null) {
426 Type parentType = getType(typeIdentification);
427
428 if ((currentType != null) && (parentType != null)
429 && (currentType != parentType)) {
430 currentVariant = new VariantImpl(atts.getValue("variant"));
431
432 currentType.addParent(parentType);
433 log.debug(">>> add IDENTITY from currentType="
434 + currentType + " to " + parentType);
435
436 addTransformation(currentType, parentType, currentVariant,
437 TypeTransformation.IDENTITY);
438 } else {
439 System.err.println("Parent type '" + typeIdentification
440 + "' does not exists yet or is same as current: "
441 + currentType);
442 }
443 }
444 } else if ("part-of".equals(qName)) {
445 String typeIdentification = null;
446
447 for (int i = 0; i < atts.getLength(); i++) {
448 String aName = atts.getQName(i);
449
450 if (aName.startsWith("by-")) {
451 String typeType = aName.substring("by-".length());
452 String identification = atts.getValue(i);
453
454 typeIdentification = typeType + ":" + identification;
455
456 break;
457 }
458 }
459
460 String contextIdentification = atts.getValue("context");
461
462 if ((typeIdentification != null) && (contextIdentification != null)) {
463 TypeContextImpl context = new TypeContextImpl(
464 contextIdentification);
465 Type parentType = getType(typeIdentification);
466
467 if ((currentType != null) && (parentType != null)
468 && (context != null) && (currentType != parentType)) {
469 log.debug(">>> parentType= " + parentType
470 + " storing context=" + context + " currentType="
471 + currentType);
472 parentType.addTypeForContext(context, currentType);
473 } else {
474 System.err.println("Whole-doc type '" + typeIdentification
475 + "' does not exists yet or is same as current: "
476 + currentType + "for context " + context);
477 }
478 }
479 } else if ("root".equals(qName)) {
480 String name = atts.getValue("name");
481
482 if (name == null) {
483 name = "*";
484 }
485
486 String ns = atts.getValue("ns");
487
488 if (ns == null) {
489 ns = "*";
490 }
491
492 if (currentType != null) {
493 root2type.put("{" + ns + "}" + name, currentType);
494 }
495 } else if ("doctype".equals(qName)) {
496 String name = atts.getValue("name");
497
498 if (name == null) {
499 name = "";
500 }
501
502 String pub = atts.getValue("public");
503
504 if (pub == null) {
505 pub = "";
506 }
507
508 String syst = atts.getValue("system");
509
510 if (syst == null) {
511 syst = "";
512 }
513
514 if (currentType != null) {
515
516 doctype2type.put(pub, currentType);
517 }
518 } else if ("transformation".equals(qName)) {
519 for (int i = 0; i < atts.getLength(); i++) {
520 String aName = atts.getQName(i);
521
522 if (aName.startsWith("source-")) {
523 String typeType = aName.substring("source-".length());
524 String identification = atts.getValue(i);
525 String typeIdentification = typeType + ":" + identification;
526
527 currentSrcType = getType(typeIdentification);
528 } else if (aName.startsWith("target-")) {
529 String typeType = aName.substring("target-".length());
530 String identification = atts.getValue(i);
531 String typeIdentification = typeType + ":" + identification;
532
533 currentTargetType = getType(typeIdentification);
534 }
535 }
536
537 currentVariant = new VariantImpl(atts.getValue("variant"));
538
539 currentTransformation = null;
540 currentMultiTransformation = null;
541 } else if ("filter".equals(qName)) {
542 String className = atts.getValue("class");
543
544
545
546 try {
547 if (currentTransformation != null) {
548
549 if (currentMultiTransformation == null) {
550
551 currentMultiTransformation = new MultiFilterTypeTransformation();
552 }
553
554
555 currentMultiTransformation
556 .addTransformation(currentTransformation);
557 }
558
559 currentTransformation = newTransformationForFilterClassName(className);
560 } catch (Exception cnfe) {
561 cnfe.printStackTrace();
562 }
563 } else if ("style".equals(qName)) {
564
565 String styleHref = atts.getValue("href");
566 String styleType = atts.getValue("type");
567
568 try {
569 if (currentTransformation != null) {
570
571 if (currentMultiTransformation == null) {
572
573 currentMultiTransformation = new MultiFilterTypeTransformation();
574 }
575
576
577 currentMultiTransformation
578 .addTransformation(currentTransformation);
579 }
580
581 if ((styleType == null) || styleType.startsWith("xsl")) {
582 currentTransformation = newTransformationForStyleSystemId(styleHref);
583 } else if (styleType.startsWith("stx")
584 || styleType.startsWith("joost")) {
585 currentTransformation = newTransformationForSTXStyleSystemId(styleHref);
586 }
587
588
589
590 } catch (TransformerConfigurationException tce) {
591
592 tce.printStackTrace();
593
594
595
596
597 }
598 } else if ("param".equals(qName)) {
599 if (currentTransformation != null) {
600 String pName = atts.getValue("name");
601
602
603 String pValue = atts.getValue("value");
604
605 currentTransformation.setParameter(pName, pValue);
606 }
607 }
608
609 super.startElement(uri, localName, qName, atts);
610 }
611
612 /***
613 * Filter an end element event.
614 *
615 * @param uri The element's Namespace URI, or the empty string.
616 * @param localName The element's local name, or the empty string.
617 * @param qName The element's qualified (prefixed) name, or the empty
618 * string.
619 * @exception SAXException The client may throw an exception during
620 * processing.
621 */
622 public void endElement(String uri, String localName, String qName)
623 throws SAXException {
624 if ("type".equals(qName)) {
625 currentType = null;
626 } else if ("transformation".equals(qName)) {
627 if (currentMultiTransformation == null) {
628 addTransformation(currentSrcType, currentTargetType,
629 currentVariant, currentTransformation);
630 } else {
631 currentMultiTransformation
632 .addTransformation(currentTransformation);
633 addTransformation(currentSrcType, currentTargetType,
634 currentVariant, currentMultiTransformation);
635 }
636 } else if ("filter".equals(qName)) {
637 } else if ("style".equals(qName)) {
638 } else if ("param".equals(qName)) {
639 }
640
641 super.endElement(uri, localName, qName);
642 }
643
644 /***
645 * Filter a character data event.
646 *
647 * @param ch An array of characters.
648 * @param start The starting position in the array.
649 * @param length The number of characters to use from the array.
650 * @exception SAXException The client may throw an exception during
651 * processing.
652 */
653 public void characters(char[] ch, int start, int length)
654 throws SAXException {
655 super.characters(ch, start, length);
656 }
657
658 /***
659 * DOCUMENT ME!
660 *
661 * @param source DOCUMENT ME!
662 * @param target DOCUMENT ME!
663 * @param variant DOCUMENT ME!
664 * @return DOCUMENT ME!
665 */
666 public List getTransformationSequence(final Type source, final Type target,
667 Variant variant) {
668
669 List sequence = new ArrayList();
670
671
672
673 SortedSet typeNodes = new TreeSet();
674 Map types2TypeNodes = new HashMap();
675
676
677 TypeNode pivot = new TypeNode(source);
678
679 pivot.costsFromSource = 0;
680 pivot.wasPivot = true;
681 types2TypeNodes.put(source, pivot);
682
683 do {
684
685
686
687
688 TransformationsFromType transFromPivot = transformations
689 .getTransformationsFrom(pivot.typ);
690
691
692 log.info("\n\nEXAMINING transf from pivot=" + pivot.typ
693 + " costs to pivot=" + pivot.costsFromSource
694 + " ... transFromPivot=" + transFromPivot);
695
696
697 if (transFromPivot == null) {
698 ;
699 }
700
701
702 else {
703 for (Iterator i = transFromPivot.typesIterator(); i.hasNext();) {
704
705 Type tg = (Type) i.next();
706
707
708 TypeNode tnOrig = (TypeNode) types2TypeNodes.get(tg);
709
710 TypeTransformation ts = transFromPivot.getTransformationTo(
711 tg, variant);
712
713
714
715 if (ts == null) {
716 continue;
717 }
718
719 TypeNode tn = (tnOrig == null) ? new TypeNode(tg) : tnOrig;
720
721 log.info(" TRANSF TO " + tg + " is " + ts);
722
723
724 if (!tn.wasPivot) {
725 if (tnOrig != null) {
726
727 typeNodes.remove(tnOrig);
728 }
729
730 if ((pivot.costsFromSource + ts.getCosts()) < tn.costsFromSource) {
731
732
733 tn.costsFromSource = pivot.costsFromSource
734 + ts.getCosts();
735 tn.previous = pivot;
736 tn.transFromPrevious = ts;
737
738
739
740
741
742 }
743
744
745 typeNodes.add(tn);
746 types2TypeNodes.put(tg, tn);
747 }
748
749
750 }
751 }
752
753
754 if (typeNodes.isEmpty()) {
755 return sequence;
756 }
757
758
759 pivot = (TypeNode) typeNodes.first();
760 pivot.wasPivot = true;
761
762 typeNodes.remove(pivot);
763
764
765
766
767 } while (!pivot.typ.equals(target));
768
769 if (pivot.typ.equals(target)) {
770
771
772
773 TypeNode p = pivot;
774
775 while ((p != null) && (p.previous != null)) {
776 sequence.add(0, p.transFromPrevious);
777
778
779 p = p.previous;
780
781
782
783
784
785 }
786 }
787
788 return sequence;
789 }
790 }
791
792 class TypeNode implements Comparable {
793 /*** DOCUMENT ME! */
794 protected TypeNode previous;
795
796 /*** DOCUMENT ME! */
797 protected Type typ;
798
799 /*** DOCUMENT ME! */
800 protected TypeTransformation transFromPrevious;
801
802 /*** DOCUMENT ME! */
803 protected double costsFromSource;
804
805 /*** DOCUMENT ME! */
806 protected boolean wasPivot;
807
808 /***
809 * Creates a new TypeNode object.
810 *
811 * @param t DOCUMENT ME!
812 */
813 public TypeNode(Type t) {
814 typ = t;
815 transFromPrevious = null;
816 costsFromSource = java.lang.Double.POSITIVE_INFINITY;
817 previous = null;
818 wasPivot = false;
819 }
820
821 /***
822 * Creates a new TypeNode object.
823 *
824 * @param t DOCUMENT ME!
825 * @param ts DOCUMENT ME!
826 */
827 public TypeNode(Type t, TypeTransformation ts) {
828 this(t);
829 transFromPrevious = ts;
830 }
831
832 /***
833 * DOCUMENT ME!
834 *
835 * @param o DOCUMENT ME!
836 * @return DOCUMENT ME!
837 */
838 public boolean equals(Object o) {
839 TypeNode tn = (TypeNode) o;
840
841 return typ.equals(tn.typ);
842 }
843
844 /***
845 * DOCUMENT ME!
846 *
847 * @return DOCUMENT ME!
848 */
849 public int hashCode() {
850 return typ.hashCode();
851 }
852
853 /***
854 * DOCUMENT ME!
855 *
856 * @param o DOCUMENT ME!
857 * @return DOCUMENT ME!
858 */
859 public int compareTo(Object o) {
860 TypeNode te = (TypeNode) o;
861
862 return ((int) (costsFromSource - te.costsFromSource) * 10)
863 + (equals(o) ? 0 : 1);
864 }
865
866 /***
867 * DOCUMENT ME!
868 *
869 * @return DOCUMENT ME!
870 */
871 public String toString() {
872 return "TypeNode[typ=" + typ + " previousTyp="
873 + ((previous == null) ? null : previous.typ)
874 + " transFromPrevious=" + transFromPrevious
875 + " costsFromSource=" + costsFromSource;
876 }
877 }
878
879
880
881
882
883
884
885
886
887
888