KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > n3 > N3JenaWriterPP


1 /*
2  * (c) Copyright 2001, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
3  * [See end of file]
4  */

5
6 package com.hp.hpl.jena.n3;
7
8 //import org.apache.commons.logging.*;
9
import com.hp.hpl.jena.rdf.model.*;
10 import com.hp.hpl.jena.shared.JenaException;
11 import com.hp.hpl.jena.vocabulary.RDF ;
12 import com.hp.hpl.jena.vocabulary.RDFS ;
13 import com.hp.hpl.jena.util.iterator.*;
14
15 import java.util.* ;
16
17 /** An N3 pretty printer.
18  * Tries to make N3 data look readable - works better on regular data.
19  *
20  * @author Andy Seaborne
21  * @version $Id: N3JenaWriterPP.java,v 1.17 2005/02/21 12:04:05 andy_seaborne Exp $
22  */

23
24
25
26 public class N3JenaWriterPP extends N3JenaWriterCommon
27     /*implements RDFWriter*/
28 {
29     // This N3 writer proceeds in 2 stages. First, it analysises the model to be
30
// written to extract information that is going to be specially formatted
31
// (RDF lists, small anon nodes) and to calculate the prefixes that will be used.
32

33     final private boolean doObjectListsAsLists = getBooleanValue("objectLists", true) ;
34     
35     // Data structures used in controlling the formatting
36

37     Set rdfLists = null ; // Heads of daml lists
38
Set rdfListsAll = null ; // Any resources in a daml lists
39
Set rdfListsDone = null ; // RDF lists written
40
Set roots = null ; // Things to put at the top level
41
Set oneRefObjects = null ; // Bnodes referred to once as an object - can inline
42
Set oneRefDone = null ; // Things done - so we can check for missed items
43

44     // Do we do nested (one reference) nodes?
45
boolean allowDeep = true ;
46     
47     static final String JavaDoc objectListSep = " , " ;
48     
49     // ----------------------------------------------------
50
// Prepatation stage
51

52     protected void prepare(Model model)
53     {
54         prepareLists(model) ;
55         prepareOneRefBNodes(model) ;
56     }
57
58     // Find well-formed RDF lists - does not find empty lists (this is intentional)
59
// Works by finding all tails, and work backwards to the head.
60
// RDF lists may, or may not, have a type element.
61
// Should do this during preparation, not as objects found during the write
62
// phase.
63

64     private void prepareLists(Model model)
65     {
66         Set thisListAll = new HashSet();
67
68         StmtIterator listTailsIter = model.listStatements(null, RDF.rest, RDF.nil);
69
70         // For every tail of a list
71
tailLoop:
72         for ( ; listTailsIter.hasNext() ; )
73         {
74             // The resource for the current element being considered.
75
Resource listElement = listTailsIter.nextStatement().getSubject() ;
76             // The resource pointing to the link we have just looked at.
77
Resource validListHead = null ;
78
79             // Chase to head of list
80
for ( ; ; )
81             {
82                 boolean isOK = checkListElement(listElement) ;
83                 if ( ! isOK )
84                     break ;
85
86                 // At this point the element is exactly a DAML list element.
87
if ( N3JenaWriter.DEBUG ) out.println("# RDF list all: "+formatResource(listElement)) ;
88                 validListHead = listElement ;
89                 thisListAll.add(listElement) ;
90
91                 // Find the previous node.
92
StmtIterator sPrev = model.listStatements(null, RDF.rest, listElement) ;
93
94                 if ( ! sPrev.hasNext() )
95                     // No daml:rest link
96
break ;
97
98                 // Valid pretty-able list. Might be longer.
99
listElement = sPrev.nextStatement().getSubject() ;
100                 if ( sPrev.hasNext() )
101                 {
102                     if ( N3JenaWriter.DEBUG ) out.println("# RDF shared tail from "+formatResource(listElement)) ;
103                     break ;
104                 }
105             }
106             // At head of a pretty-able list - add its elements and its head.
107
if ( N3JenaWriter.DEBUG ) out.println("# DAML list head: "+formatResource(validListHead)) ;
108             rdfListsAll.addAll(thisListAll) ;
109             if ( validListHead != null )
110                 rdfLists.add(validListHead) ;
111         }
112         listTailsIter.close() ;
113     }
114
115     // Validate one list element.
116
private boolean checkListElement(Resource listElement)
117     {
118         if (!listElement.hasProperty(RDF.rest)
119             || !listElement.hasProperty(RDF.first))
120         {
121             if (N3JenaWriter.DEBUG)
122                 out.println(
123                     "# RDF list element does not have required properties: "
124                         + formatResource(listElement));
125             return false;
126         }
127
128         // Must be exactly two properties (the ones we just tested for)
129
// or three including the RDF.type RDF.List statement.
130
int numProp = countProperties(listElement);
131
132         if ( numProp == 2)
133             // Must have exactly the properties we just tested for.
134
return true ;
135
136
137         if (numProp == 3)
138         {
139             if (listElement.hasProperty(RDF.type, RDF.List))
140                 return true;
141             if (N3JenaWriter.DEBUG)
142                 out.println(
143                     "# RDF list element: 3 properties but no rdf:type rdf:List"
144                         + formatResource(listElement));
145             return false;
146         }
147
148         if (N3JenaWriter.DEBUG)
149             out.println(
150                 "# RDF list element does not right number of properties: "
151                     + formatResource(listElement));
152         return false;
153     }
154
155     // Find bnodes that are objects of only one statement (and hence can be inlined)
156
// which are not RDF lists.
157
// Could do this testing at write time (unlike lists)
158

159     private void prepareOneRefBNodes(Model model)
160     {
161
162         NodeIterator objIter = model.listObjects() ;
163         for ( ; objIter.hasNext() ; )
164         {
165             RDFNode n = objIter.nextNode() ;
166             
167             if ( testOneRefBNode(n) )
168                 oneRefObjects.add(n) ;
169             objIter.close() ;
170
171             // N3JenaWriter.DEBUG
172
if ( N3JenaWriter.DEBUG )
173             {
174                 out.println("# RDF Lists = "+rdfLists.size()) ;
175                 out.println("# RDF ListsAll = "+rdfListsAll.size()) ;
176                 out.println("# oneRefObjects = "+oneRefObjects.size()) ;
177             }
178         }
179     }
180     
181     private boolean testOneRefBNode(RDFNode n)
182     {
183         if ( ! ( n instanceof Resource ) )
184             return false ;
185
186         Resource obj = (Resource)n ;
187
188         if ( ! obj.isAnon() )
189             return false ;
190
191         // In a list - done as list, not as embedded bNode.
192
if ( rdfListsAll.contains(obj) )
193             // RDF list (head or element)
194
return false ;
195
196         StmtIterator pointsToIter = obj.getModel().listStatements(null, null, obj) ;
197         if ( ! pointsToIter.hasNext() )
198             // Corrupt graph!
199
throw new JenaException("N3: found object with no arcs!") ;
200
201         Statement s = pointsToIter.nextStatement() ;
202                
203         if ( pointsToIter.hasNext() )
204             return false ;
205
206         if ( N3JenaWriter.DEBUG )
207             out.println("# OneRef: "+formatResource(obj)) ;
208         return true ;
209     }
210   
211     // ----------------------------------------------------
212
// Output stage
213

214     // Property order is:
215
// 1 - rdf:type (as "a")
216
// 2 - other rdf: rdfs: namespace items (sorted)
217
// 3 - all other properties, sorted by URI (not qname)
218

219
220     
221     protected ClosableIterator preparePropertiesForSubject(Resource r)
222     {
223         Set seen = new HashSet() ;
224         boolean hasTypes = false ;
225         SortedMap tmp1 = new TreeMap() ;
226         SortedMap tmp2 = new TreeMap() ;
227         
228         StmtIterator sIter = r.listProperties();
229         for ( ; sIter.hasNext() ; )
230         {
231             Property p = sIter.nextStatement().getPredicate() ;
232             if ( seen.contains(p) )
233                 continue ;
234             seen.add(p) ;
235             
236             if ( p.equals(RDF.type) )
237             {
238                 hasTypes = true ;
239                 continue ;
240             }
241             
242             if ( p.getURI().startsWith(RDF.getURI()) ||
243                  p.getURI().startsWith(RDFS.getURI()) )
244             {
245                 tmp1.put(p.getURI(), p) ;
246                 continue ;
247             }
248             
249             tmp2.put(p.getURI(), p) ;
250         }
251         sIter.close() ;
252         
253         ExtendedIterator eIter = null ;
254         
255         if ( hasTypes )
256             eIter = new SingletonIterator(RDF.type) ;
257
258         ExtendedIterator eIter2 = WrappedIterator.create(tmp1.values().iterator()) ;
259             
260         eIter = (eIter == null) ? eIter2 : eIter.andThen(eIter2) ;
261                     
262         eIter2 = WrappedIterator.create(tmp2.values().iterator()) ;
263         
264         eIter = (eIter == null) ? eIter2 : eIter.andThen(eIter2) ;
265         return eIter ;
266     }
267     
268     protected boolean skipThisSubject(Resource subj)
269     {
270         return rdfListsAll.contains(subj) ||
271                oneRefObjects.contains(subj) ;
272     }
273
274 // protected void writeModel(Model model)
275
// {
276
// super.writeModel(model) ;
277
//
278
//
279

280     // Before ...
281

282     protected void startWriting()
283     {
284         allocateDatastructures() ;
285     }
286
287     // Flush any unwritten objects.
288
// 1 - OneRef objects
289
// Normally there are "one ref" objects left
290
// However loops of "one ref" are possible.
291
// 2 - Lists
292

293     protected void finishWriting()
294     {
295         oneRefObjects.removeAll(oneRefDone);
296
297         for (Iterator leftOverIter = oneRefObjects.iterator(); leftOverIter.hasNext();)
298         {
299             out.println();
300             if (N3JenaWriter.DEBUG)
301                 out.println("# One ref");
302             // Don't allow further one ref objects to be inlined.
303
allowDeep = false;
304             writeOneGraphNode((Resource) leftOverIter.next());
305             allowDeep = true;
306         }
307
308         // Are there any unattached RDF lists?
309
// We missed these earlier (assumed all DAML lists are values of some statement)
310
for (Iterator leftOverIter = rdfLists.iterator(); leftOverIter.hasNext();)
311         {
312             Resource r = (Resource) leftOverIter.next();
313             if (rdfListsDone.contains(r))
314                 continue;
315             out.println();
316             if (N3JenaWriter.DEBUG)
317                 out.println("# RDF List");
318                 
319             if (!r.isAnon() || countArcsTo(r) > 0 )
320             {
321                 // Name it.
322
out.print(formatResource(r));
323                 out.print(" :- ");
324             }
325             writeList(r);
326             out.println(" .");
327         }
328
329         //out.println() ;
330
//writeModelSimple(model, bNodesMap, base) ;
331
out.flush();
332         clearDatastructures() ;
333     }
334
335
336
337     // Need to decide between one line or many.
338
// Very hard to do a pretty thing here because the objects may be large or small or a mix.
339

340     protected void writeObjectList(Resource resource, Property property)
341     {
342 // if ( ! doObjectListsAsLists )
343
// {
344
// super.writeObjectList(resource, property) ;
345
// return ;
346
// }
347

348         String JavaDoc propStr = formatProperty(property);
349         
350         // Find which objects are simple (i.e. not nested structures)
351

352         StmtIterator sIter = resource.listProperties(property);
353         Set simple = new HashSet() ;
354         Set complex = new HashSet() ;
355
356         for (; sIter.hasNext();)
357         {
358             Statement stmt = sIter.nextStatement();
359             RDFNode obj = stmt.getObject() ;
360             if ( isSimpleObject(obj) )
361                 simple.add(obj) ;
362             else
363                 complex.add(obj) ;
364         }
365         sIter.close() ;
366         // DEBUG
367
int simpleSize = simple.size() ;
368         int complexSize = complex.size() ;
369         
370         // Write property/simple objects
371

372         if ( simple.size() > 0 )
373         {
374             String JavaDoc padSp = null ;
375             // Simple objects - allow property to be long and alignment to be lost
376
if ((propStr.length()+minGap) <= widePropertyLen)
377                 padSp = pad(calcPropertyPadding(propStr)) ;
378             
379             if ( doObjectListsAsLists )
380             {
381                 // Write all simple objects as one list.
382
out.print(propStr);
383                 out.incIndent(indentObject) ;
384             
385                 if ( padSp != null )
386                     out.print(padSp) ;
387                 else
388                     out.println() ;
389             
390                 for (Iterator iter = simple.iterator(); iter.hasNext();)
391                 {
392                     RDFNode n = (RDFNode) iter.next();
393                     writeObject(n);
394                     
395                     // As an object list
396
if (iter.hasNext())
397                         out.print(objectListSep);
398                 }
399                 
400                 out.decIndent(indentObject) ;
401             }
402             else
403             {
404                 for (Iterator iter = simple.iterator(); iter.hasNext();)
405                 {
406                     // This is also the same as the complex case
407
// except the width the property can go in is different.
408
out.print(propStr);
409                     out.incIndent(indentObject) ;
410                     if ( padSp != null )
411                         out.print(padSp) ;
412                     else
413                         out.println() ;
414                     
415                     RDFNode n = (RDFNode) iter.next();
416                     writeObject(n);
417                     out.decIndent(indentObject) ;
418                     
419                     // As an object list
420
if (iter.hasNext())
421                         out.println(" ;");
422                    }
423                 
424             }
425         }
426         // Now do complex objects.
427
// Write property each time for a complex object.
428
// Do not allow over long properties but same line objects.
429

430         if (complex.size() > 0)
431         {
432             // Finish the simple list if there was one
433
if ( simple.size() > 0 )
434                 out.println(" ;");
435             
436             int padding = -1 ;
437             String JavaDoc padSp = null ;
438             
439             // Can we fit teh start of teh complex object on this line?
440

441             // DEBUG variable.
442
int tmp = propStr.length() ;
443             // Complex objects - do not allow property to be long and alignment to be lost
444
if ((propStr.length()+minGap) <= propertyCol)
445             {
446                 padding = calcPropertyPadding(propStr) ;
447                 padSp = pad(padding) ;
448             }
449
450             for (Iterator iter = complex.iterator(); iter.hasNext();)
451             {
452                 int thisIndent = indentObject ;
453                 //if ( i )
454
out.incIndent(thisIndent);
455                 out.print(propStr);
456                 if ( padSp != null )
457                     out.print(padSp) ;
458                 else
459                     out.println() ;
460             
461                 RDFNode n = (RDFNode) iter.next();
462                 writeObject(n);
463                 out.decIndent(thisIndent);
464                 if ( iter.hasNext() )
465                     out.println(" ;");
466             }
467         }
468         return;
469     }
470
471
472     private boolean isSimpleObject(RDFNode node)
473     {
474         if (node instanceof Literal)
475             return true ;
476         Resource rObj = (Resource) node;
477         if ( allowDeep && oneRefObjects.contains(rObj) )
478             return false ;
479         return true ;
480     }
481
482     protected void writeObject(RDFNode node)
483     {
484         if (node instanceof Literal)
485         {
486             writeLiteral((Literal) node);
487             return;
488         }
489
490         Resource rObj = (Resource) node;
491         if ( allowDeep && ! isSimpleObject(rObj))
492         {
493             oneRefDone.add(rObj);
494             //int oldIndent = out.getIndent();
495
//out.setIndent(out.getCol());
496

497             //out.incIndent(4);
498
//out.println();
499
out.print("[ ");
500             out.incIndent(2);
501             writePropertiesForSubject(rObj);
502             out.decIndent(2);
503             out.println() ;
504             // Line up []
505
out.print("]");
506             //out.decIndent(4);
507

508             //out.setIndent(oldIndent);
509
return ;
510         }
511
512         if (rdfLists.contains(rObj))
513             if (countArcsTo(rObj) <= 1)
514             {
515                 writeList(rObj);
516                 return;
517             }
518
519         out.print(formatResource(rObj));
520     }
521
522
523
524     // Need to out.print in short (all on one line) and long forms (multiple lines)
525
// That needs starts point depth tracking.
526
private void writeList(Resource resource)
527         
528     {
529         out.print( "(");
530         out.incIndent(2) ;
531         boolean listFirst = true;
532         for (Iterator iter = rdfListIterator(resource); iter.hasNext();)
533         {
534             if (!listFirst)
535                 out.print( " ");
536             listFirst = false;
537             RDFNode n = (RDFNode) iter.next();
538             writeObject(n) ;
539         }
540         out.print( ")");
541         out.decIndent(2) ;
542         rdfListsDone.add(resource);
543
544     }
545
546     // Called before each writing run.
547
protected void allocateDatastructures()
548     {
549         rdfLists = new HashSet() ;
550         rdfListsAll = new HashSet() ;
551         rdfListsDone = new HashSet() ;
552         oneRefObjects = new HashSet() ;
553         oneRefDone = new HashSet() ;
554     }
555
556     // Especially release large intermediate memory objects
557
protected void clearDatastructures()
558     {
559         rdfLists = null ;
560         rdfListsAll = null ;
561         rdfListsDone = null ;
562         oneRefObjects = null ;
563         oneRefDone = null ;
564     }
565 }
566
567 /*
568  * (c) Copyright 2001, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
569  * All rights reserved.
570  *
571  * Redistribution and use in source and binary forms, with or without
572  * modification, are permitted provided that the following conditions
573  * are met:
574  * 1. Redistributions of source code must retain the above copyright
575  * notice, this list of conditions and the following disclaimer.
576  * 2. Redistributions in binary form must reproduce the above copyright
577  * notice, this list of conditions and the following disclaimer in the
578  * documentation and/or other materials provided with the distribution.
579  * 3. The name of the author may not be used to endorse or promote products
580  * derived from this software without specific prior written permission.
581  *
582  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
583  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
584  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
585  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
586  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
587  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
588  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
589  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
590  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
591  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
592  */

593
Popular Tags