KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > xmloutput > impl > Unparser


1 /*
2  * (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
3  * All rights reserved.
4  * [See end of file]
5  * $Id: Unparser.java,v 1.34 2005/02/21 12:22:32 andy_seaborne Exp $
6  */

7
8 package com.hp.hpl.jena.xmloutput.impl;
9
10 /*
11  * @author Jeremy Carroll
12  *
13  * Want todo List
14  * - easy efficiency gains in listSubjects() and modelListSubjects()
15  * by removing those subjects that we have already considered.
16
17  * - Set Default language during first pass.
18  *
19  *
20  *Notes on ID and BagID:
21  *Our preferences are follows:
22  * for a Stating with an explicit local ID we avoid explicitly
23  * constructing the reification, and try and use rule 6.12 with an idAttr.
24  * If the Stating is anonymous or non-local then we construct the reification
25  * explicitly.
26  *
27  *
28  * Notes:
29  * The following rules are not supported by the current Jena RDF parser:
30  * 6.8
31  *
32  *
33  * [6.1] RDF ::= ['<rdf:RDF>'] obj* ['</rdf:RDF>']
34  * [6.2] obj ::= description | container
35  * [6.3] description ::= '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '/>'
36  * | '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '>'
37  * propertyElt* '</rdf:Description>'
38  * | typedNode
39  [6.4] container ::= sequence | bag | alternative
40  [6.5] idAboutAttr ::= idAttr | aboutAttr | aboutEachAttr
41  [6.6] idAttr ::= ' ID="' IDsymbol '"'
42  [6.7] aboutAttr ::= ' about="' URI-reference '"'
43 [6.8] aboutEachAttr ::= ' aboutEach="' URI-reference '"'
44                         | ' aboutEachPrefix="' string '"'
45  [6.9] bagIdAttr ::= ' bagID="' IDsymbol '"'
46  [6.10] propAttr ::= typeAttr
47                           | propName '="' string '"' (with embedded quotes escaped)
48  [6.11] typeAttr ::= ' type="' URI-reference '"'
49  [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
50                          | '<' propName idAttr? parseLiteral '>'
51                                literal '</' propName '>'
52                          | '<' propName idAttr? parseResource '>'
53                                propertyElt* '</' propName '>'
54                          | '<' propName idRefAttr? bagIdAttr? propAttr* '/>'
55
56  [daml.1 - 6.12 cont.] | '<' propName idAttr? parseDamlCollection '>'
57                             obj*
58                             '</' propName '>'
59  [daml.2] parseDamlCollection ::= ' parseType="daml:collection"'
60
61  [6.13] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr* '/>'
62                           | '<' typeName idAboutAttr? bagIdAttr? propAttr* '>'
63  * propertyElt* '</' typeName '>'
64  * [6.14] propName ::= Qname
65  * [6.15] typeName ::= Qname
66  * [6.16] idRefAttr ::= idAttr | resourceAttr
67  * [6.17] value ::= obj | string
68  * [6.18] resourceAttr ::= ' resource="' URI-reference '"'
69  * [6.19] Qname ::= [ NSprefix ':' ] name
70  * [6.20] URI-reference ::= string, interpreted per [URI]
71  * [6.21] IDsymbol ::= (any legal XML name symbol)
72  * [6.22] name ::= (any legal XML name symbol)
73  * [6.23] NSprefix ::= (any legal XML namespace prefix)
74  * [6.24] string ::= (any XML text, with "<", ">", and "&" escaped)
75  * [6.25] sequence ::= '<rdf:Seq' idAttr? '>' member* '</rdf:Seq>'
76  * | '<rdf:Seq' idAttr? memberAttr* '/>'
77  * [6.26] bag ::= '<rdf:Bag' idAttr? '>' member* '</rdf:Bag>'
78  * | '<rdf:Bag' idAttr? memberAttr* '/>'
79  * [6.27] alternative ::= '<rdf:Alt' idAttr? '>' member+ '</rdf:Alt>'
80  * | '<rdf:Alt' idAttr? memberAttr? '/>'
81  * [6.28] member ::= referencedItem | inlineItem
82  * [6.29] referencedItem ::= '<rdf:li' resourceAttr '/>'
83  * [6.30] inlineItem ::= '<rdf:li' '>' value </rdf:li>'
84  * | '<rdf:li' parseLiteral '>' literal </rdf:li>'
85  * | '<rdf:li' parseResource '>' propertyElt* </rdf:li>'
86  * [6.31] memberAttr ::= ' rdf:_n="' string '"' (where n is an integer)
87  * [6.32] parseLiteral ::= ' parseType="Literal"'
88  * [6.33] parseResource ::= ' parseType="Resource"'
89  * [6.34] literal ::= (any well-formed XML)
90  *
91  */

92 import com.hp.hpl.jena.rdf.model.*;
93 import com.hp.hpl.jena.util.iterator.*;
94 import com.hp.hpl.jena.vocabulary.*;
95 import com.hp.hpl.jena.rdf.model.impl.*;
96 import com.hp.hpl.jena.rdf.arp.*;
97 import com.hp.hpl.jena.shared.*;
98
99 import java.util.*;
100 import java.io.*;
101
102 import org.apache.commons.logging.Log;
103 import org.apache.commons.logging.LogFactory;
104 import org.apache.xerces.util.XMLChar;
105
106 /** An Unparser will output a model in the abbreviated syntax.
107  ** @version Release='$Name: $' Revision='$Revision: 1.34 $' Date='$Date: 2005/02/21 12:22:32 $'
108
109  */

110 class Unparser {
111     static private Property LI = new PropertyImpl(RDF.getURI(), "li");
112     static private Property DESCRIPTION =
113         new PropertyImpl(RDF.getURI(), "Description");
114
115     static protected Log logger = LogFactory.getLog(Unparser.class);
116
117     /** Creates an Unparser for the specified model.
118      * The localName is the URI (typical URL) intended for
119      * the output file. No trailing "#" should be used.
120      * This will control the use of <I>ID</I> or <I>about</I> or <I>resource</I>
121      * on various rules.
122      * @param localName The intended URI of the output file. No trailing "#".
123      * @param m The model.
124      * @param w The output.
125      */

126     Unparser(Abbreviated parent, String JavaDoc localName, Model m, PrintWriter w) {
127         setLocalName(localName);
128         prettyWriter = parent;
129         out = w;
130         model = m;
131         addTypeNameSpaces();
132         objectTable = new HashMap();
133         StmtIterator ss = m.listStatements();
134         try {
135             while (ss.hasNext()) {
136                 Statement s = ss.nextStatement();
137                 RDFNode rn = s.getObject();
138                 if (rn instanceof Resource) {
139                     increaseObjectCount((Resource) rn);
140                 }
141             }
142         } finally {
143             ss.close();
144         }
145         try {
146             res2statement = new HashMap();
147             statement2res = new HashMap();
148             ClosableIterator reified = new MapFilterIterator(new MapFilter() {
149                 public Object JavaDoc accept(Object JavaDoc o) {
150                     Resource r = (Resource) o;
151                     return (
152                         r.hasProperty(RDF.subject)
153                             && r.hasProperty(RDF.object)
154                             && r.hasProperty(RDF.predicate))
155                         ? r
156                         : null;
157
158                 }
159             }, model.listSubjectsWithProperty(RDF.type, RDF.Statement));
160             while (reified.hasNext()) {
161                 Resource r = (Resource) reified.next();
162                 try {
163                     /**
164                      * This block of code assumes that really we
165                      * are dealing with a reification.
166                      * We may, on the contrary, be dealing with a random
167                      * collection of triples that do not make sense.
168                      */

169                     Statement subj = r.getRequiredProperty(RDF.subject);
170                     Statement pred = r.getRequiredProperty(RDF.predicate);
171                     Statement obj = r.getRequiredProperty(RDF.object);
172                     RDFNode nobj = obj.getObject();
173                     Resource rsubj = (Resource) subj.getObject();
174                     Resource rpred = (Resource) pred.getObject();
175
176                     Property ppred =
177                         model.createProperty(((Resource) rpred).getURI());
178
179                     Statement statement =
180                         model.createStatement((Resource) rsubj, ppred, nobj);
181                     res2statement.put(r, statement);
182                     statement2res.put(statement, r);
183                 } catch (Exception JavaDoc ignored) {
184                 }
185             }
186         } finally {
187             ss.close();
188         }
189     }
190
191     /**
192         Note: must work with uri being null.
193     */

194     private void setLocalName(String JavaDoc uri) {
195         if (uri == null || uri.equals(""))
196             localName = "";
197         else
198             try {
199                 URI u = new URI(uri);
200                 u.setFragment(null);
201                 localName = u.getURIString();
202             } catch (MalformedURIException e) {
203                 throw new BadURIException( uri, e);
204             }
205     }
206
207     /** Should be called exactly once for each Unparser.
208      * Calling it a second time will have undesired results.
209      */

210     void write() {
211         prettyWriter.workOutNamespaces();
212         wRDF();
213         /*
214         System.out.print("Coverage = ");
215         for (int i=0;i<codeCoverage.length;i++)
216             System.out.print(" c[" + i + "] = " + codeCoverage[i]+ ";");
217         System.out.println();
218          **/

219     }
220     /** Set a list of types of objects that will be expanded at the
221      * top-level of the file.
222      * @param types An array of rdf:Class'es.
223      *
224      */

225     void setTopLevelTypes(Resource types[]) {
226         pleasingTypes = types;
227         pleasingTypeSet = new HashSet(Arrays.asList(types));
228     }
229
230     private String JavaDoc xmlBase;
231     void setXMLBase(String JavaDoc b) {
232         xmlBase = b;
233     }
234     /* THE MORE INTERESTING MEMBER VARIABLES.
235      * Note there are others scattered throughout the file,
236      * but those are only used by one or two methods.
237      */

238
239     final private static String JavaDoc rdfns = RDF.type.getNameSpace();
240     final private static Integer JavaDoc one = new Integer JavaDoc(1);
241
242     private String JavaDoc localName;
243     private Map objectTable; // This is a map from Resource to Integer
244
// which indicates how many times each resource
245
// occurs as an object of a triple.
246
private Model model;
247     private PrintWriter out;
248     private Set doing = new HashSet(); // Some of the resources that
249
// are currently being written.
250
private Set doneSet = new HashSet(); // The triples that have been output.
251
private Set haveReified = new HashSet(); // Those local resources that are
252
// the id's of a reification, used to ensure that anonymous
253
// resources are made non-anonymous when reified in certain ways.
254

255     private Resource pleasingTypes[] = null;
256     private Set pleasingTypeSet = new HashSet();
257
258     final private Abbreviated prettyWriter;
259
260     private boolean avoidExplicitReification = true;
261     // We set this to false as we start giving up on elegance.
262

263     // Reification stuff.
264

265     Map res2statement;
266     Map statement2res;
267
268     /* The top-down recursive descent unparser.
269      * The methods starting in w all refer to one of the rules
270      * of the grammar, which they implement.
271      * boolean valued rules first check whether they are applicable
272      * and return false if not. Otherwise they create appropriate output
273      * (using recursive descent) and return true. Note all necessary
274        checks are made before any output or any recursive descent.
275      * The void w- methods just implement the rule, which typically does not
276      * involve any choice.
277      */

278     /*
279     [6.1] RDF ::= ['<rdf:RDF>'] obj* ['</rdf:RDF>']
280      */

281     private void wRDF() {
282         tab();
283         print("<");
284         print(prettyWriter.rdfEl("RDF"));
285         indentPlus();
286         printNameSpaceDefn();
287         if (xmlBase != null) {
288             setLocalName(xmlBase);
289             tab();
290             print("xml:base=" + quote(xmlBase));
291         }
292         print(">");
293         wObjStar();
294         indentMinus();
295         tab();
296         print("</");
297         print(prettyWriter.rdfEl("RDF"));
298         print(">");
299         tab();
300     }
301
302     /**
303      * All subjects get listed, for top level use only.
304      */

305     private void wObjStar() {
306         Iterator rs = listSubjects();
307         while (rs.hasNext()) {
308             Resource r = (Resource) rs.next();
309             increaseObjectCount(r);
310             // This forces us to not be anonymous unless
311
// we are never an object. See isGenuineAnon().
312
wObj(r, true);
313         }
314         closeAllResIterators();
315     }
316
317     /*
318     [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
319                            | '<' propName idAttr? parseLiteral '>'
320                                  literal '</' propName '>'
321                            | '<' propName idAttr? parseResource '>'
322                                  propertyElt* '</' propName '>'
323                            | '<' propName idRefAttr? bagIdAttr? propAttr* '/>'
324     [daml.1 - 6.12 cont.] | '<' propName idAttr? parseDamlCollection '>'
325                               obj*
326                               '</' propName '>'
327     [daml.2] parseDamlCollection ::= ' parseType="daml:collection"'
328     
329     For daml collections we prefer the special syntax otherwise:
330     We prefer choice 4 where possible, except in the case where the
331     statement is reified and the object is not anonymous in which case
332     we use one of the others (e.g. choice 1).
333     For embedded XML choice 2 is obligatory.
334     For untyped, anonymous resource valued items choice 3 is used.
335     Choice 1 is the fall back.
336      */

337     private boolean wPropertyElt(
338         WType wt,
339         Property prop,
340         Statement s,
341         RDFNode val) {
342         return wPropertyEltCompact(wt, prop, s, val) || // choice 4
343
wPropertyEltDamlCollection(wt, prop, s, val) || // choice daml.1
344
wPropertyEltLiteral(wt, prop, s, val) || // choice 2
345
wPropertyEltResource(wt, prop, s, val) || // choice 3
346
wPropertyEltDatatype(wt, prop, s, val)
347             || wPropertyEltValue(wt, prop, s, val);
348         // choice 1.
349
}
350
351     /* [6.12.4] propertyElt ::= '<' propName idRefAttr? bagIdAttr? propAttr* '/>'
352      */

353     private boolean wPropertyEltCompact(
354         WType wt,
355         Property prop,
356         Statement s,
357         RDFNode val) {
358         // Conditions
359
if (!(val instanceof Resource))
360             return false;
361         Resource r = (Resource) val;
362         if (!(allPropsAreAttr(r) || doing.contains(r)))
363             return false;
364         // '<' propName '/>' is 6.12.1 rather than 6.12.4
365
// and it becomes an empty string value.
366
// Whether this is a mistake or not is debatable.
367
// We avoid the construction.
368
if ((!hasProperties(r)) && isGenuineAnon(r))
369             return false;
370         // Write out
371
done(s);
372         tab();
373         print("<");
374         wt.wTypeStart(prop);
375         indentPlus();
376         wIdRefAttrOpt(s, r);
377         if (!doing.contains(r)) {
378             wPropAttrAll(r);
379         } else if (isGenuineAnon(r)) {
380             // ???
381
error("Genuine anon resource in cycle?");
382         }
383         indentMinus();
384         print("/>");
385         return true;
386     }
387
388     /*
389     [6.12.2] propertyElt ::= '<' propName idAttr? parseLiteral '>'
390                                  literal '</' propName '>'
391      */

392     private boolean wPropertyEltLiteral(
393         WType wt,
394         Property prop,
395         Statement s,
396         RDFNode r) {
397         if (prettyWriter.sParseTypeLiteralPropertyElt)
398             return false;
399         if (!((r instanceof Literal) && ((Literal) r).getWellFormed())) {
400             return false;
401         }
402         // print out.
403
done(s);
404         tab();
405         print("<");
406         wt.wTypeStart(prop);
407         wIdAttrReified(s);
408         maybeNewline();
409         wParseLiteral();
410         maybeNewline();
411         print(">");
412         print(((Literal) r).getLexicalForm());
413         print("</");
414         wt.wTypeEnd(prop);
415         print(">");
416         return true;
417     }
418
419     private boolean wPropertyEltDatatype(
420         WType wt,
421         Property prop,
422         Statement s,
423         RDFNode r) {
424         if (!((r instanceof Literal)
425             && ((Literal) r).getDatatypeURI() != null)) {
426             return false;
427         }
428         // print out.
429
done(s);
430         tab();
431         print("<");
432         wt.wTypeStart(prop);
433         wIdAttrReified(s);
434         maybeNewline();
435         wDatatype(((Literal) r).getDatatypeURI());
436         maybeNewline();
437         print(">");
438         print(
439             Util.substituteEntitiesInElementContent(
440                 ((Literal) r).getLexicalForm()));
441         print("</");
442         wt.wTypeEnd(prop);
443         print(">");
444         return true;
445     }
446
447     /*
448     [6.12.3] propertyElt ::= '<' propName idAttr? parseResource '>'
449                                  propertyElt* '</' propName '>'
450      */

451     private boolean wPropertyEltResource(
452         WType wt,
453         Property prop,
454         Statement s,
455         RDFNode r) {
456         if (prettyWriter.sParseTypeResourcePropertyElt)
457             return false;
458         if (r instanceof Literal)
459             return false;
460         Resource res = (Resource) r;
461         if (!isGenuineAnon(res))
462             return false;
463         if (getType(res) != null)
464             return false; // preferred typed node construction.
465
// print out.
466
done(s);
467         tab();
468         print("<");
469         wt.wTypeStart(prop);
470         indentPlus();
471         wIdAttrReified(s);
472         wParseResource();
473         print(">");
474         wPropertyEltStar(res);
475         indentMinus();
476         tab();
477         print("</");
478         wt.wTypeEnd(prop);
479         print(">");
480         return true;
481     }
482
483     /*
484     [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
485      */

486     private boolean wPropertyEltValue(
487         WType wt,
488         Property prop,
489         Statement s,
490         RDFNode r) {
491         return wPropertyEltValueString(wt, prop, s, r)
492             || wPropertyEltValueObj(wt, prop, s, r);
493     }
494
495     /*
496     [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
497      */

498     private boolean wPropertyEltValueString(
499         WType wt,
500         Property prop,
501         Statement s,
502         RDFNode r) {
503         if (r instanceof Literal) {
504             done(s);
505             Literal lt = (Literal) r;
506             String JavaDoc lang = lt.getLanguage();
507             tab();
508             print("<");
509             wt.wTypeStart(prop);
510             wIdAttrReified(s);
511             maybeNewline();
512             if (lang != null && lang.length() > 0)
513                 print(" xml:lang=" + q(lang));
514             maybeNewline();
515             print(">");
516             wValueString(lt);
517             print("</");
518             wt.wTypeEnd(prop);
519             print(">");
520             return true;
521         } else {
522             return false;
523         }
524     }
525
526     /*
527     [6.17.2] value ::= string
528      */

529     private void wValueString(Literal lt) {
530         String JavaDoc val = lt.getString();
531         print(Util.substituteEntitiesInElementContent(val));
532     }
533
534     /*
535     [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
536     [6.17.1] value ::= obj
537      */

538     private boolean wPropertyEltValueObj(
539         WType wt,
540         Property prop,
541         Statement s,
542         RDFNode r) {
543         if (r instanceof Resource && !prettyWriter.sResourcePropertyElt) {
544             Resource res = (Resource) r;
545             done(s);
546             tab();
547             print("<");
548             wt.wTypeStart(prop);
549             wIdAttrReified(s);
550             print(">");
551             tab();
552             indentPlus();
553             wObj(res, false);
554             indentMinus();
555             tab();
556             print("</");
557             wt.wTypeEnd(prop);
558             print(">");
559             return true;
560         } else {
561             return false;
562         }
563     }
564
565     /*
566     [daml.1 - 6.12 cont.] | '<' propName idAttr? parseDamlCollection '>'
567                               obj*
568                               '</' propName '>'
569      */

570     private boolean wPropertyEltDamlCollection(
571         WType wt,
572         Property prop,
573         Statement s,
574         RDFNode r) {
575         boolean daml = true;
576         Statement list[][] = getDamlList(r);
577         if (list == null) {
578             daml = false;
579             list = getRDFList(r);
580         }
581         if (list == null)
582             return false;
583         // print out.
584
done(s);
585         // record all done's first - they may impact the
586
// way we print the values.
587
for (int i = 0; i < list.length; i++) {
588             done(list[i][0]);
589             done(list[i][1]);
590             if (daml)
591                 done(list[i][2]);
592         }
593         tab();
594         print("<");
595         wt.wTypeStart(prop);
596         indentPlus();
597         wIdAttrReified(s);
598         if (daml)
599             wParseDamlCollection();
600         else
601             wParseCollection();
602
603         print(">");
604         for (int i = 0; i < list.length; i++) {
605             wObj((Resource) list[i][0].getObject(), false);
606         }
607         indentMinus();
608         tab();
609         print("</");
610         wt.wTypeEnd(prop);
611         print(">");
612         return true;
613     }
614
615     // propAttr* with no left over statements.
616
private void wPropAttrAll(Resource r) {
617         wPropAttrSome(r);
618         if (hasProperties(r))
619             error("Bad call to wPropAttrAll");
620     }
621
622     // propAttr* possibly with left over statements.
623
private void wPropAttrSome(Resource r) {
624         ClosableIterator ss = listProperties(r);
625         try {
626             Set seen = new HashSet();
627             while (ss.hasNext()) {
628                 Statement s = (Statement) ss.next();
629                 RDFNode val = s.getObject();
630                 if (canBeAttribute(s, seen)) {
631                     done(s);
632                     wPropAttr(s.getPredicate(), s.getObject());
633                 }
634             }
635         } finally {
636             ss.close();
637         }
638     }
639
640     /*
641     [6.2] obj ::= description | container
642     [6.3] description ::= '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '/>'
643                            | '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '>'
644                                   propertyElt* '</rdf:Description>'
645                            | typedNode
646     [6.4] container ::= sequence | bag | alternative
647     We use:
648     [6.2a] obj ::= description | container | typedNode
649     [6.3a] description ::= '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '/>'
650                            | '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '>'
651                                   propertyElt* '</rdf:Description>'
652      *
653      * This method has got somewhat messy. If we are not at the topLevel we
654      * may choose to not expand a node but just use a
655      * typedNode ::= '<' typeName idAboutAttr '/>'
656      * rule.
657      * This rules also applies to Bags that we feel unconfortable with,
658      * such as a Bag arising from a BagId rule that we don't handle properly.
659      *
660     
661      */

662     private boolean wObj(Resource r, boolean topLevel) {
663         try {
664             doing.add(r);
665             Statement typeStatement = getType(r);
666             if (typeStatement != null) {
667                 Resource t = typeStatement.getResource();
668                 if (!topLevel) {
669                     if (pleasingTypeSet.contains(t) && (!isGenuineAnon(r))) {
670                         return wTypedNodeNoProperties(r);
671                     }
672                 }
673                 return wTypedNode(r) || wDescription(r);
674             } else
675                 return wDescription(r);
676         } finally {
677             doing.remove(r);
678         }
679     }
680
681     abstract private class WType {
682         abstract void wTypeStart(Resource uri);
683         abstract void wTypeEnd(Resource uri);
684     }
685
686     static private int RDF_HASH = RDF.getURI().length();
687
688     private WType wdesc = new WType() {
689         void wTypeStart(Resource u) {
690             print(prettyWriter.rdfEl(u.getURI().substring(RDF_HASH)));
691         }
692         void wTypeEnd(Resource u) {
693             print(prettyWriter.rdfEl(u.getURI().substring(RDF_HASH)));
694         }
695     };
696
697     private WType wtype = new WType() {
698         void wTypeStart(Resource u) {
699             print(prettyWriter.startElementTag(u.getURI()));
700         }
701         void wTypeEnd(Resource u) {
702             print(prettyWriter.endElementTag(u.getURI()));
703         }
704     };
705
706     /*
707     [6.3a] description ::= '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '/>'
708                            | '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '>'
709                                   propertyElt* '</rdf:Description>'
710      */

711     private boolean wDescription(Resource r) {
712         return wTypedNodeOrDescription(wdesc, DESCRIPTION, r);
713     }
714     /*
715     [6.13] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr* '/>'
716                            | '<' typeName idAboutAttr? bagIdAttr? propAttr* '>'
717                                  propertyElt* '</' typeName '>'
718      */

719     private boolean wTypedNode(Resource r) {
720         Statement st = getType(r);
721         if (st == null)
722             return false;
723         Resource type = st.getResource();
724         done(st);
725         return wTypedNodeOrDescription(wtype, type, r);
726     }
727
728     private boolean wTypedNodeOrDescription(
729         WType wt,
730         Resource ty,
731         Resource r) {
732         // preparation - look for the li's.
733
Vector found = new Vector();
734         ClosableIterator ss = listProperties(r);
735         try {
736             int greatest = 0;
737             if (!prettyWriter.sListExpand)
738                 while (ss.hasNext()) {
739                     Statement s = (Statement) ss.next();
740                     int ix = s.getPredicate().getOrdinal();
741                     if (ix != 0) {
742                         if (ix > greatest) {
743                             found.setSize(ix);
744                             greatest = ix;
745                         }
746                         found.set(ix - 1, s);
747                     }
748                 }
749         } finally {
750             ss.close();
751         }
752         int last = found.indexOf(null);
753         List li = last == -1 ? found : found.subList(0, last);
754
755         return wTypedNodeOrDescriptionCompact(wt, ty, r, li)
756             || wTypedNodeOrDescriptionLong(wt, ty, r, li);
757     }
758
759     /* [6.13.1] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr* '/>'
760      */

761     private boolean wTypedNodeOrDescriptionCompact(
762         WType wt,
763         Resource ty,
764         Resource r,
765         List li) {
766         // Conditions
767
if ((!li.isEmpty()) || !allPropsAreAttr(r))
768             return false;
769         // Write out
770
tab();
771         print("<");
772         wt.wTypeStart(ty);
773         indentPlus();
774         wIdAboutAttrOpt(r);
775         wPropAttrAll(r);
776         print("/>");
777         indentMinus();
778         return true;
779     }
780
781     /* [6.13.1] typedNode ::= '<' typeName idAboutAttr '/>'
782      */

783     private boolean wTypedNodeNoProperties(Resource r) {
784         // Conditions
785
if (isGenuineAnon(r))
786             return false;
787         Statement st = getType(r);
788         if (st == null)
789             return false;
790         Resource type = st.getResource();
791         done(st);
792         // Write out
793
tab();
794         print("<");
795         wtype.wTypeStart(type);
796         indentPlus();
797     // if (hasProperties(r))
798
// wAboutAttr(r);
799
// else
800
wIdAboutAttrOpt(r);
801         print("/>");
802         indentMinus();
803         return true;
804     }
805
806     /*
807     [6.13.2] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr* '>'
808                                  propertyElt* '</' typeName '>'
809      */

810     private boolean wTypedNodeOrDescriptionLong(
811         WType wt,
812         Resource ty,
813         Resource r,
814         List li) {
815         Iterator it = li.iterator();
816         while (it.hasNext()) {
817             done((Statement) it.next());
818         }
819
820         tab();
821         print("<");
822         wt.wTypeStart(ty);
823         indentPlus();
824         wIdAboutAttrOpt(r);
825         wPropAttrSome(r);
826         print(">");
827         wLiEltStar(li.iterator());
828         wPropertyEltStar(r);
829         indentMinus();
830         tab();
831         print("</");
832         wt.wTypeEnd(ty);
833         print(">");
834         return true;
835     }
836
837     private void wPropertyEltStar(Resource r) {
838         ClosableIterator ss = this.listProperties(r);
839         try {
840             while (ss.hasNext()) {
841                 Statement s = (Statement) ss.next();
842                 wPropertyElt(wtype, s.getPredicate(), s, s.getObject());
843             }
844         } finally {
845             ss.close();
846         }
847     }
848
849     private void wLiEltStar(Iterator ss) {
850         while (ss.hasNext()) {
851             Statement s = (Statement) ss.next();
852             wPropertyElt(wdesc, LI, s, s.getObject());
853         }
854     }
855
856     /*
857     [6.5] idAboutAttr ::= idAttr | aboutAttr | aboutEachAttr
858     we use
859     [6.5a] idAboutAttr ::= idAttr | aboutAttr
860      */

861     private Set idDone = new HashSet();
862
863     private boolean wIdAboutAttrOpt(Resource r) {
864         return wIdAttrOpt(r) || wNodeIDAttr(r) || wAboutAttr(r);
865     }
866
867     /**
868      * Returns false if the resource is not genuinely anonymous and cannot
869      * be referred to using an ID.
870      * [6.6] idAttr ::= ' ID="' IDsymbol '"'
871      */

872     private boolean wIdAttrOpt(Resource r) {
873
874         if (isGenuineAnon(r))
875             return true; // We have output resource (with nothing).
876
if (prettyWriter.sIdAttr)
877             return false;
878         if (r.isAnon())
879             return false;
880         if (isLocalReference(r)) {
881             // Try and use the reification rules if they apply.
882
// Issue: aren't we just about to list those statements explicitly.
883
if (wantReification(r))
884                 return false;
885             // Can be an ID if not already output.
886
if (idDone.contains(r)) {
887                 return false; // We have already output this one.
888
} else {
889                 idDone.add(r);
890                 print(" ");
891                 printRdfAt("ID");
892                 print("=");
893                 print(quote(getLocalName(r)));
894                 return true;
895             }
896         } else {
897             return false;
898         }
899     }
900
901     /*
902     [6.7] aboutAttr ::= ' about="' URI-reference '"'
903      */

904     private boolean wAboutAttr(Resource r) {
905         print(" ");
906         printRdfAt("about");
907         print("=");
908         wURIreference(r);
909         return true;
910     }
911
912     private void wURIreference(String JavaDoc s) {
913         print(quote(prettyWriter.relativize(s)));
914     }
915
916     private void wURIreference(Resource r) {
917         wURIreference(r.getURI());
918     }
919
920     /*
921     [6.16] idRefAttr ::= idAttr | resourceAttr
922      */

923     private void wIdRefAttrOpt(Statement s, Resource r) {
924         wIdAttrReified(s);
925         if (!isGenuineAnon(r)) {
926             wResourceNodeIDAttr(r);
927         }
928     }
929
930     /*
931     [6.6] idAttr ::= ' ID="' IDsymbol '"'
932      */

933     private void wIdAttrReified(Statement s) {
934         if (wantReification(s)) {
935             /*
936             if ( prettyWriter.sReification )
937               System.err.println("???");
938             else
939               System.err.println("!!!");
940             */

941             Statement reify[] = reification(s);
942             Resource res = (Resource) statement2res.get(s);
943             idDone.add(res);
944             int i;
945             for (i = 0; i < reify.length; i++)
946                 done(reify[i]);
947             print(" ");
948             printRdfAt("ID");
949             print("=");
950             print(quote(getLocalName(res)));
951             haveReified.add(res);
952         }
953     }
954
955     /*
956     [6.18] resourceAttr ::= ' resource="' URI-reference '"'
957      */

958     private boolean wResourceNodeIDAttr(Resource r) {
959         return wNodeIDAttr(r) || wResourceAttr(r);
960     }
961
962     /*
963      nodeIDAttr ::= ' rdf:nodeID="' URI-reference '"'
964      */

965     private boolean wNodeIDAttr(Resource r) {
966         if (!r.isAnon())
967             return false;
968         print(" ");
969         printRdfAt("nodeID");
970         print("=");
971         print(q(prettyWriter.anonId(r)));
972
973         return true;
974     }
975
976     /*
977     [6.18] resourceAttr ::= ' resource="' URI-reference '"'
978      */

979     private boolean wResourceAttr(Resource r) {
980         if (r.isAnon())
981             return false;
982         print(" ");
983         printRdfAt("resource");
984         print("=");
985         wURIreference(r);
986         return true;
987     }
988
989     int codeCoverage[] = new int[8];
990
991     /*
992      [6.19] Qname ::= [ NSprefix ':' ] name
993      
994     private void wQnameStart(String ns, String local) {
995         print(prettyWriter.startElementTag(ns, local));
996     }
997
998     private void wQnameEnd(String ns, String local) {
999         print(prettyWriter.endElementTag(ns, local));
1000    }
1001*/

1002    private void wQNameAttr(Property p) {
1003        print(prettyWriter.attributeTag(p.getURI()));
1004    }
1005
1006    private void printRdfAt(String JavaDoc s) {
1007        print(prettyWriter.rdfAt(s));
1008    }
1009
1010    /*
1011    [6.10] propAttr ::= typeAttr
1012                           | propName '="' string '"' (with embedded quotes escaped)
1013    [6.11] typeAttr ::= ' type="' URI-reference '"'
1014     */

1015    private void wPropAttr(Property p, RDFNode n) {
1016        tab();
1017        if (p.equals(RDF.type))
1018            wTypeAttr((Resource) n);
1019        else
1020            wPropAttrString(p, (Literal) n);
1021    }
1022
1023    private void wTypeAttr(Resource r) {
1024        print(" ");
1025        printRdfAt("type");
1026        print("=");
1027        wURIreference(r);
1028        //print(quote(r.getURI()));
1029
}
1030
1031    private void wPropAttrString(Property p, Literal l) {
1032        print(" ");
1033        wQNameAttr(p);
1034        print("=" + quote(l.getString()));
1035    }
1036
1037    /*
1038    [daml.2] parseDamlCollection ::= ' parseType="daml:collection"'
1039     */

1040    private void wParseDamlCollection() {
1041        print(" ");
1042        printRdfAt("parseType");
1043        print("=" + q("daml:collection"));
1044    }
1045
1046    /*
1047    [List.2] parseCollection ::= ' parseType="Collection"'
1048     */

1049    private void wParseCollection() {
1050        print(" ");
1051        printRdfAt("parseType");
1052        print("=" + q("Collection"));
1053    }
1054
1055    /*
1056    [6.32] parseLiteral ::= ' parseType="Literal"'
1057     */

1058    private void wParseLiteral() {
1059        print(" ");
1060        printRdfAt("parseType");
1061        print("=" + q("Literal"));
1062    }
1063
1064    private void wDatatype(String JavaDoc dtURI) {
1065        print(" ");
1066        printRdfAt("datatype");
1067        print("=");
1068        maybeNewline();
1069        wURIreference(dtURI);
1070    }
1071    /*
1072    [6.33] parseResource ::= ' parseType="Resource"'
1073     */

1074    private void wParseResource() {
1075        print(" ");
1076        printRdfAt("parseType");
1077        print("=" + q("Resource"));
1078    }
1079
1080    private void printNameSpaceDefn() {
1081        print(prettyWriter.xmlnsDecl());
1082    }
1083
1084    /****************************************************************************
1085     * Utility routines ...
1086     *
1087     ***************************************************************************/

1088
1089    /***
1090     * Output and indentation.
1091     ***/

1092    private int indentLevel = 0;
1093
1094    private int currentColumn = 0;
1095
1096    static private String JavaDoc filler(int lgth) {
1097        char rslt[] = new char[lgth];
1098        Arrays.fill(rslt, ' ');
1099        return new String JavaDoc(rslt);
1100    }
1101
1102    private void tab() {
1103        int desiredColumn = prettyWriter.tab * indentLevel;
1104        if (desiredColumn > prettyWriter.width) {
1105            desiredColumn = 4 + (desiredColumn - 4) % prettyWriter.width;
1106        }
1107        if ((desiredColumn == 0 && currentColumn == 0)
1108            || desiredColumn > currentColumn) {
1109            String JavaDoc spaces = filler(desiredColumn - currentColumn);
1110            out.print(spaces);
1111        } else {
1112            out.println();
1113            out.print(filler(desiredColumn));
1114        }
1115        currentColumn = desiredColumn;
1116    }
1117
1118    private void maybeNewline() {
1119        if (currentColumn > prettyWriter.width) {
1120            tab();
1121        }
1122    }
1123
1124    /**
1125     * Quote str with either ' or " quotes to be in attribute position
1126     * in XML.
1127        The real rules are found at http://www.w3.org/TR/REC-xml#AVNormalize
1128     */

1129    private String JavaDoc quote(String JavaDoc str) {
1130        return prettyWriter.qq(str);
1131    }
1132    private String JavaDoc q(String JavaDoc str) {
1133        return prettyWriter.q(str);
1134    }
1135
1136    /**
1137     * Indentation screws up if there is a tab character in s.
1138     * We do not check this.
1139     */

1140    private void print(String JavaDoc s) {
1141        out.print(s);
1142        int ix = s.lastIndexOf('\n');
1143        if (ix == -1)
1144            currentColumn += s.length();
1145        else
1146            currentColumn = s.length() - ix - 1;
1147    }
1148
1149    private void indentPlus() {
1150        indentLevel++;
1151    }
1152
1153    private void indentMinus() {
1154        indentLevel--;
1155    }
1156
1157    /* Unexpected error.
1158     */

1159    private void error(String JavaDoc msg) {
1160        JenaException e =
1161            new BrokenException("Internal error in Unparser: " + msg);
1162        this.prettyWriter.fatalError(e);
1163        throw e; // Just in case.
1164
}
1165    /**
1166     * Name space stuff.
1167     **/

1168    private void addTypeNameSpaces() {
1169        NodeIterator nn = model.listObjectsOfProperty(RDF.type);
1170        try {
1171            while (nn.hasNext()) {
1172                RDFNode obj = nn.nextNode();
1173                int split = isOKType(obj);
1174                if (split != -1)
1175                    prettyWriter.addNameSpace(
1176                        ((Resource) obj).getURI().substring(0, split));
1177            }
1178        } finally {
1179            nn.close();
1180        }
1181    }
1182
1183    private String JavaDoc getNameSpace(Resource r) {
1184        if (r.isAnon()) {
1185            logger.error("Internal error - Unparser.getNameSpace; giving up");
1186            throw new BrokenException("Internal error: getNameSpace(bNode)");
1187        }
1188            String JavaDoc uri = r.getURI();
1189            int split = Util.splitNamespace(uri);
1190            return uri.substring(0, split);
1191        
1192    }
1193
1194    /**
1195     * Local and/or anonymous resources.
1196     **/

1197    private boolean isGenuineAnon(Resource r) {
1198        if (!r.isAnon())
1199            return false;
1200        Integer JavaDoc v = (Integer JavaDoc) objectTable.get(r);
1201        return v == null
1202            || ((!prettyWriter.sResourcePropertyElt)
1203                && v.intValue() <= 1
1204                && (!haveReified.contains(r)));
1205    }
1206
1207    private boolean isLocalReference(Resource r) {
1208        return (!r.isAnon())
1209            && getNameSpace(r).equals(localName + "#")
1210            && XMLChar.isValidNCName(getLocalName(r));
1211    }
1212    /*
1213     * Utility for turning an integer into an alphabetic string.
1214     
1215    private static String getSuffix(int suffixId) {
1216        if (suffixId == 0)
1217            return "";
1218        else {
1219            suffixId--;
1220            int more = (suffixId / 26);
1221
1222            return getSuffix(more)
1223                + new Character((char) ('a' + suffixId % 26));
1224        }
1225    }
1226    */

1227
1228    private String JavaDoc getLocalName(Resource r) {
1229        if (r.isAnon()) {
1230            logger.error("Internal error - giving up - Unparser.getLocalName");
1231            throw new BrokenException("Internal error: getLocalName(bNode)");
1232        } else {
1233            String JavaDoc uri = r.getURI();
1234            int split = Util.splitNamespace(uri);
1235            return uri.substring(split);
1236        }
1237    }
1238    /**
1239     * objectTable initialization.
1240     */

1241
1242    private void increaseObjectCount(Resource r) {
1243        if (!r.isAnon())
1244            return;
1245        Integer JavaDoc cnt = (Integer JavaDoc) objectTable.get(r);
1246        if (cnt == null) {
1247            cnt = one;
1248        } else {
1249            cnt = new Integer JavaDoc(cnt.intValue() + 1);
1250        }
1251        objectTable.put(r, cnt);
1252    }
1253
1254    /***
1255     * Reification support.
1256     ****/

1257    /*
1258      Is the use of ID in rule [6.12] to create a reification helpful or not?
1259     */

1260    private boolean wantReification(Statement s) {
1261        return wantReification(s, (Resource) statement2res.get(s));
1262    }
1263
1264    private boolean wantReification(Resource res) {
1265        return wantReification((Statement) res2statement.get(res), res);
1266    }
1267
1268    private boolean wantReification(Statement s, Resource ref) {
1269        if (s == null
1270            || ref == null
1271            || ref.isAnon()
1272            || prettyWriter.sReification)
1273            return false;
1274        if (!(isLocalReference(ref)))
1275            return false;
1276        Statement reify[] = reification(s);
1277        int i;
1278        for (i = 0; i < reify.length; i++)
1279            if (doneSet.contains(reify[i]) || (!model.contains(reify[i])))
1280                return false; // Some of reification already done.
1281
return true; // Reification rule helps.
1282
}
1283
1284    private Statement[] reification(Statement s) {
1285        Model m = s.getModel();
1286        Resource r = (Resource) statement2res.get(s);
1287        return new Statement[] {
1288            m.createStatement(r, RDF.type, RDF.Statement),
1289            m.createStatement(r, RDF.subject, s.getSubject()),
1290            m.createStatement(r, RDF.predicate, s.getPredicate()),
1291            m.createStatement(r, RDF.object, s.getObject())};
1292    }
1293
1294    private boolean hasProperties(Resource r) {
1295        ExtendedIterator ss = listProperties(r);
1296        if (avoidExplicitReification
1297            && // ( r instanceof Statement ) &&
1298
(!r.isAnon())
1299            && isLocalReference(r)
1300            && res2statement.containsKey(r)) {
1301            ss = new MapFilterIterator(new MapFilter() {
1302                public Object JavaDoc accept(Object JavaDoc o) {
1303                    Statement s = (Statement) o;
1304                    Property p = s.getPredicate();
1305                    String JavaDoc local = p.getLocalName();
1306                    return (
1307                        (!p.getNameSpace().equals(rdfns))
1308                            || !((RDF.type.equals(p)
1309                                && s.getObject().equals(RDF.Statement))
1310                                || RDF.object.equals(p)
1311                                || RDF.predicate.equals(p)
1312                                || RDF.subject.equals(p)))
1313                        ? o
1314                        : null;
1315                }
1316            }, ss);
1317        }
1318        try {
1319            return ss.hasNext();
1320        } finally {
1321            ss.close();
1322        }
1323    }
1324    private ExtendedIterator listProperties(Resource r) {
1325        return new MapFilterIterator(new MapFilter() {
1326            public Object JavaDoc accept(Object JavaDoc o) {
1327                return doneSet.contains(o) ? null : o;
1328            }
1329        }, r.listProperties());
1330    }
1331    // Good type statement, or simple string valued statement with no langID
1332
// See http://www.w3.org/TR/REC-xml#AVNormalize
1333
private boolean canBeAttribute(Statement s, Set seen) {
1334        Property p = s.getPredicate();
1335        // Check seen first.
1336
if (prettyWriter.sPropertyAttr
1337            || seen.contains(p)) // We can't use the same attribute
1338
// twice in one rule.
1339
return false;
1340        seen.add(p);
1341
1342        if (p.equals(RDF.type)) {
1343            // If we have a model in which a type is given
1344
// as a string, then we avoid the attribute rule 6.10 which is
1345
// ambiguous with 6.11.
1346
RDFNode n = s.getObject();
1347            return (n instanceof Resource) && !((Resource) n).isAnon();
1348        }
1349
1350        if (s.getObject() instanceof Literal) {
1351            Literal l = s.getLiteral();
1352            if (l.getDatatypeURI() != null)
1353                return false;
1354
1355            if (l.getLanguage().equals("")) {
1356                // j.cook.up bug fix
1357
if (prettyWriter.isDefaultNamespace(getNameSpace(p)))
1358                    return false;
1359
1360                String JavaDoc str = l.getString();
1361                if (str.length() < 40) {
1362                    char buf[] = str.toCharArray();
1363                    for (int i = 0; i < buf.length; i++) {
1364                        // See http://www.w3.org/TR/REC-xml#AVNormalize
1365
if (buf[i] <= ' ')
1366                            return false;
1367                    }
1368                    return !wantReification(s);
1369                }
1370            }
1371        }
1372        return false;
1373    }
1374
1375    private boolean allPropsAreAttr(Resource r) {
1376        ClosableIterator ss = listProperties(r);
1377        Set seen = new HashSet();
1378        try {
1379            while (ss.hasNext()) {
1380                Statement s = (Statement) ss.next();
1381                if (!canBeAttribute(s, seen))
1382                    return false;
1383            }
1384        } finally {
1385            ss.close();
1386        }
1387        return true;
1388    }
1389    private void done(Statement s) {
1390        doneSet.add(s);
1391        // return false;
1392
}
1393    /** If r represent a daml:collection return a 2D array of its statements.
1394     * For each member there are three statements the first gives the DAML.first
1395     * statement, the second the DAML.rest statement and the third the RDF.type
1396     * statement.
1397     * @return null on failure or the elements of the collection.
1398     *
1399     */

1400    private Statement[][] getDamlList(RDFNode r) {
1401        return prettyWriter.sDamlCollection
1402            ? null
1403            : getList(
1404                r,
1405                DAML_OIL.List,
1406                DAML_OIL.first,
1407                DAML_OIL.rest,
1408                DAML_OIL.nil);
1409    }
1410    private Statement[][] getRDFList(RDFNode r) {
1411        return prettyWriter.sParseTypeCollectionPropertyElt
1412            ? null
1413            : getList(r, null, RDF.first, RDF.rest, RDF.nil);
1414    }
1415
1416    private Statement[][] getList(
1417        RDFNode r,
1418        Resource list,
1419        Property first,
1420        Property rest,
1421        Resource nil) {
1422        boolean lookingGood = false;
1423
1424        Vector rslt = new Vector();
1425        Set seen = new HashSet();
1426        RDFNode next = r;
1427        // We walk down the list and check each member.
1428
try {
1429
1430            while (!next.equals(nil)) {
1431                Statement elt[] = new Statement[list == null ? 2 : 3];
1432                if (next instanceof Literal)
1433                    return null;
1434                Resource res = (Resource) next;
1435                // We cannot label the nodes in the daml:collection construction.
1436
if (!isGenuineAnon(res))
1437                    return null;
1438                // The occurs check - cyclic loop rather than a list.
1439
if (seen.contains(next))
1440                    return null;
1441                seen.add(next);
1442
1443                // We must have exactly three properties.
1444
StmtIterator ss = res.listProperties();
1445                try {
1446                    while (ss.hasNext()) {
1447                        Statement s = ss.nextStatement();
1448                        Property p = s.getPredicate();
1449                        int ix;
1450                        RDFNode obj = s.getObject();
1451                        if (doneSet.contains(s))
1452                            return null;
1453                        if (!(obj instanceof Resource)) {
1454                            return null;
1455                        }
1456                        if (p.equals(RDF.type)) {
1457                            ix = 2;
1458                            if (!obj.equals(list))
1459                                return null;
1460                        } else if (p.equals(first)) {
1461                            ix = 0;
1462                            lookingGood = true;
1463                        } else if (p.equals(rest)) {
1464                            ix = 1;
1465                            next = obj;
1466                        } else {
1467                            return null;
1468                        }
1469                        if (elt[ix] != null)
1470                            return null;
1471                        elt[ix] = s;
1472                    }
1473                } finally {
1474                    ss.close();
1475                }
1476                for (int i = 0; i < elt.length; i++)
1477                    if (elt[i] == null)
1478                        // didn't have the three required elements.
1479
return null;
1480                rslt.add(elt);
1481            }
1482            if (rslt.size() == 0)
1483                return null;
1484            lookingGood = false; // suppress bug report request
1485
} finally {
1486            /*
1487            if (lookingGood) {
1488                // try and get decent bug report
1489                logger.warn("The RDF/XML-ABBREV writer detected a partially well-formed list structure, but rejected it. "+
1490                "There have been hard to reproduce bugs concerning this. "+
1491                "Please take a copy of your output and mail it to jena-dev@yahoogroups.com"+
1492                " Even better, would be a copy of your code that produced it.") ;
1493            }
1494            */

1495        }
1496        Statement array[][] = new Statement[rslt.size()][];
1497        rslt.copyInto(array);
1498        return array;
1499
1500    }
1501    /**
1502     * @return A statement that is suitable for a typed node construction or null.
1503     */

1504    private Statement getType(Resource r) {
1505        Statement rslt;
1506        try {
1507            if (r instanceof Statement) {
1508                rslt = ((Statement) r).getStatementProperty(RDF.type);
1509                if (rslt == null || (!rslt.getObject().equals(RDF.Statement)))
1510                    error("Statement type problem");
1511            } else {
1512                rslt = r.getRequiredProperty(RDF.type);
1513            }
1514        } catch (PropertyNotFoundException rdfe) {
1515            if (r instanceof Statement)
1516                error("Statement type problem");
1517            rslt = null;
1518        }
1519        if (rslt == null || isOKType(rslt.getObject()) == -1)
1520            return null;
1521
1522        return rslt;
1523    }
1524
1525    /** @param n The value of some rdf:type (precondition).
1526     * @return The split point or -1.
1527     */

1528
1529    private int isOKType(RDFNode n) {
1530
1531        if (!(n instanceof Resource))
1532            return -1;
1533        if (((Resource) n).isAnon())
1534            return -1;
1535        // Only allow resources with namespace and fragment ID
1536
String JavaDoc uri = ((Resource) n).getURI();
1537
1538        int split = Util.splitNamespace(uri);
1539        if (split == 0 || split == uri.length())
1540            return -1;
1541
1542        return split;
1543    }
1544
1545    /**
1546     * The order of outputting the resources.
1547     * This all supports wObjStar.
1548     **/

1549    private Set infinite;
1550    private void findInfiniteCycles() {
1551        // find all statements that haven't been done.
1552
StmtIterator ss = model.listStatements();
1553        Relation relation = new Relation();
1554        try {
1555            while (ss.hasNext()) {
1556                Statement s = ss.nextStatement();
1557                if (!doneSet.contains(s)) {
1558                    RDFNode rn = s.getObject();
1559                    if (rn instanceof Resource) {
1560                        relation.set(s.getSubject(), rn);
1561                    }
1562                }
1563            }
1564        } finally {
1565            ss.close();
1566        }
1567        relation.transitiveClosure();
1568        infinite = relation.getDiagonal();
1569    }
1570    /**
1571     * This class is an iterator over the set infinite, but
1572     * we wait until it is used before instantiating the
1573     * underlying iterator.
1574     */

1575    private Iterator allInfiniteLeft() {
1576        return new LateBindingIterator() {
1577            public Iterator create() {
1578                return infinite.iterator();
1579            }
1580        };
1581    }
1582
1583    private Iterator pleasingTypeIterator() {
1584        if (pleasingTypes == null)
1585            return new NullIterator();
1586        Map buckets = new HashMap();
1587        Set bucketArray[] = new Set[pleasingTypes.length];
1588        // Set up buckets and bucketArray. Each is a collection
1589
// of the same buckets, one ordered, the other hashed.
1590
for (int i = 0; i < pleasingTypes.length; i++) {
1591            bucketArray[i] = new HashSet();
1592            buckets.put(pleasingTypes[i], bucketArray[i]);
1593        }
1594
1595        ResIterator rs = model.listSubjects();
1596        try {
1597            while (rs.hasNext()) {
1598                Resource r = rs.nextResource();
1599                Statement s = getType(r);
1600                if (s != null) {
1601                    Set bucket = (Set) buckets.get(s.getObject());
1602                    if (bucket != null) {
1603                        if (isGenuineAnon(r)) {
1604                            Integer JavaDoc v = (Integer JavaDoc) objectTable.get(r);
1605                            if (v != null && v.intValue() == 1)
1606                                continue;
1607                        }
1608                        bucket.add(r);
1609                    }
1610                }
1611            }
1612        } finally {
1613            rs.close();
1614        }
1615
1616        // Now all the pleasing resources are in the buckets.
1617
// Add all their iterators togethor:
1618

1619        return new IteratorIterator(new Map1Iterator(new Map1() {
1620            public Object JavaDoc map1(Object JavaDoc bkt) {
1621                return ((Set) bkt).iterator();
1622            }
1623        }, new ArrayIterator(bucketArray)));
1624
1625    }
1626    /**
1627     * listSubjects - generates a list of subjects for the wObjStar rule.
1628     * We wish to order these elegantly.
1629     * The current implementation goes for:
1630     * <ul>
1631     * <li> The current file - mainly intended for good DAML.
1632     * <li> Subjects that are not objects of anything, excluding reifications
1633     * <li> At these stage we evaluate a dependency graph of the remaining resources.
1634     * <li>non-anonymous resources that are the object of more than one
1635     * rule that are in infinite cycles.
1636     * <li> any non genuinely anonymous resources that are in infinite cycles
1637     * <li>any other resource in an infinite cyle
1638     * <li>any other resource.
1639     * <li>reifications
1640     *</ul>
1641     *
1642     *
1643     * At the end, we need to close any underlying ResIterators from the model,
1644     * however to avoid complications in much of this code we use general
1645     * java.util.Iterator-s. We hence use a wrapper around a ResIterator to
1646     * allow us to manage the closing issue.
1647     */

1648    private Iterator listSubjects() {
1649        // The current file - mainly intended for good DAML.
1650
Iterator currentFile =
1651            // new ArrayIterator(
1652
// new Resource[] { model.createResource(this.localName)});
1653
new SingletonIterator(model.createResource(this.localName));
1654        // The pleasing types
1655
Iterator pleasing = pleasingTypeIterator();
1656
1657        Iterator fakeStopPleasing = new NullIterator() {
1658            public boolean hasNext() {
1659                pleasingTypeSet = new HashSet();
1660                return false;
1661            }
1662        };
1663
1664        // Subjects that are not objects of anything.
1665
Iterator nonObjects = new FilterIterator(new Filter() {
1666            public boolean accept(Object JavaDoc o) {
1667                return (!objectTable.containsKey(o))
1668                    && (!wantReification((Resource) o));
1669            }
1670        }, modelListSubjects());
1671        // At these stage we evaluate a dependency graph of the remaining resources.
1672
// This is stuck in the master iterator so that it's hasNext is called
1673
// at an appropriate time (after the earlier stages, before the later stages).
1674
// We use this to trigger the dependency graph evalaution.
1675
Iterator fakeLazyEvaluator = new NullIterator() {
1676            public boolean hasNext() {
1677                    // Evalaute dependency graph.
1678
findInfiniteCycles();
1679                return false;
1680            }
1681        };
1682        // non-anonymous resources that are the object of more than one
1683
// triple that are in infinite cycles.
1684
Iterator firstChoiceCyclic = new FilterIterator(new Filter() {
1685            public boolean accept(Object JavaDoc o) {
1686                Resource r = (Resource) o;
1687                codeCoverage[4]++;
1688                if (r.isAnon())
1689                    return false;
1690                Integer JavaDoc cnt = (Integer JavaDoc) objectTable.get(r);
1691                if (cnt == null || cnt.intValue() <= 1)
1692                    return false;
1693                return true;
1694            }
1695        }, this.allInfiniteLeft());
1696        // any non genuinely anonymous resources that are in infinite cycles
1697
Iterator nonAnonInfinite = new FilterIterator(new Filter() {
1698            public boolean accept(Object JavaDoc o) {
1699                codeCoverage[5]++;
1700                Resource r = (Resource) o;
1701                return !isGenuineAnon(r);
1702            }
1703        }, allInfiniteLeft());
1704        // any other resource in an infinite cyle
1705
Iterator infinite = allInfiniteLeft();
1706        Iterator anotherFake = new NullIterator() {
1707            public boolean hasNext() {
1708                avoidExplicitReification = false;
1709                return false;
1710            }
1711        };
1712        Iterator reifications = new FilterIterator(new Filter() {
1713            public boolean accept(Object JavaDoc o) {
1714                codeCoverage[6]++;
1715                return res2statement.containsKey(o);
1716            }
1717        }, allInfiniteLeft());
1718        // any other resource.
1719
Iterator backStop = modelListSubjects();
1720
1721        Iterator all[] =
1722            new Iterator[] {
1723                currentFile,
1724                pleasing,
1725                fakeStopPleasing,
1726                nonObjects,
1727                fakeLazyEvaluator,
1728                firstChoiceCyclic,
1729                nonAnonInfinite,
1730                infinite,
1731                anotherFake,
1732                reifications,
1733                new NullIterator() {
1734                    public boolean hasNext() { if (
1735                        modelListSubjects()
1736                        .hasNext())
1737                        codeCoverage[7]++;
1738                    return false;
1739                }
1740            }, backStop };
1741        Iterator allAsOne = new IteratorIterator(new ArrayIterator(all));
1742
1743        // Filter for those that still have something to list.
1744
return new FilterIterator(new Filter() {
1745            public boolean accept(Object JavaDoc o) {
1746                return hasProperties((Resource) o);
1747            }
1748        }, allAsOne);
1749    }
1750
1751    private Set openResIterators = new HashSet();
1752
1753    private synchronized void close(ResIterator resIt) {
1754        resIt.close();
1755        openResIterators.remove(resIt);
1756    }
1757
1758    private synchronized void closeAllResIterators() {
1759        Iterator members = openResIterators.iterator();
1760        while (members.hasNext()) {
1761            ((ResIterator) members.next()).close();
1762        }
1763        openResIterators = new HashSet();
1764    }
1765
1766    private Iterator modelListSubjects() {
1767        ResIterator resIt = model.listSubjects();
1768        openResIterators.add(resIt);
1769        return resIt;
1770
1771    }
1772
1773}
1774
1775/*
1776    (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
1777    All rights reserved.
1778
1779    Redistribution and use in source and binary forms, with or without
1780    modification, are permitted provided that the following conditions
1781    are met:
1782
1783    1. Redistributions of source code must retain the above copyright
1784       notice, this list of conditions and the following disclaimer.
1785
1786    2. Redistributions in binary form must reproduce the above copyright
1787       notice, this list of conditions and the following disclaimer in the
1788       documentation and/or other materials provided with the distribution.
1789
1790    3. The name of the author may not be used to endorse or promote products
1791       derived from this software without specific prior written permission.
1792
1793    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1794    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1795    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1796    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1797    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1798    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1799    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1800    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1801    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1802    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1803*/

1804
Popular Tags