View Javadoc
1   /*
2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.xml.internal.xsom.impl.util;
27  
28  import com.sun.xml.internal.xsom.XSAnnotation;
29  import com.sun.xml.internal.xsom.XSAttGroupDecl;
30  import com.sun.xml.internal.xsom.XSAttributeDecl;
31  import com.sun.xml.internal.xsom.XSAttributeUse;
32  import com.sun.xml.internal.xsom.XSComplexType;
33  import com.sun.xml.internal.xsom.XSContentType;
34  import com.sun.xml.internal.xsom.XSElementDecl;
35  import com.sun.xml.internal.xsom.XSFacet;
36  import com.sun.xml.internal.xsom.XSIdentityConstraint;
37  import com.sun.xml.internal.xsom.XSListSimpleType;
38  import com.sun.xml.internal.xsom.XSModelGroup;
39  import com.sun.xml.internal.xsom.XSModelGroupDecl;
40  import com.sun.xml.internal.xsom.XSNotation;
41  import com.sun.xml.internal.xsom.XSParticle;
42  import com.sun.xml.internal.xsom.XSRestrictionSimpleType;
43  import com.sun.xml.internal.xsom.XSSchema;
44  import com.sun.xml.internal.xsom.XSSchemaSet;
45  import com.sun.xml.internal.xsom.XSSimpleType;
46  import com.sun.xml.internal.xsom.XSType;
47  import com.sun.xml.internal.xsom.XSUnionSimpleType;
48  import com.sun.xml.internal.xsom.XSWildcard;
49  import com.sun.xml.internal.xsom.XSXPath;
50  import com.sun.xml.internal.xsom.XSWildcard.Any;
51  import com.sun.xml.internal.xsom.XSWildcard.Other;
52  import com.sun.xml.internal.xsom.XSWildcard.Union;
53  import com.sun.xml.internal.xsom.impl.Const;
54  import com.sun.xml.internal.xsom.visitor.XSSimpleTypeVisitor;
55  import com.sun.xml.internal.xsom.visitor.XSTermVisitor;
56  import com.sun.xml.internal.xsom.visitor.XSVisitor;
57  import com.sun.xml.internal.xsom.visitor.XSWildcardFunction;
58  
59  import java.io.IOException;
60  import java.io.Writer;
61  import java.math.BigInteger;
62  import java.text.MessageFormat;
63  import java.util.Iterator;
64  
65  /**
66   * Generates approximated XML Schema representation from
67   * a schema component. This is not intended to be a fully-fledged
68   * round-trippable schema writer.
69   *
70   * <h2>Usage of this class</h2>
71   * <ol>
72   *  <li>Create a new instance with whatever Writer
73   *      you'd like to send the output to.
74   *  <li>Call one of the overloaded dump methods.
75   *      You can repeat this process as many times as you want.
76   * </ol>
77   *
78   * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
79   * @author Kirill Grouchnikov (kirillcool@yahoo.com)
80   */
81  public class SchemaWriter implements XSVisitor, XSSimpleTypeVisitor {
82      public SchemaWriter( Writer _out ) {
83          this.out=_out;
84      }
85  
86      /** output is sent to this object. */
87      private final Writer out;
88  
89      /** indentation. */
90      private int indent;
91  
92      private void println(String s) {
93          try {
94              for( int i=0; i<indent; i++)    out.write("  ");
95              out.write(s);
96              out.write('\n');
97              // flush stream to make the output appear immediately
98              out.flush();
99          } catch( IOException e ) {
100             // ignore IOException.
101             hadError = true;
102         }
103     }
104     private void println() { println(""); }
105 
106     /** If IOException is encountered, this flag is set to true. */
107     private boolean hadError =false;
108 
109     /** Flush the stream and check its error state. */
110     public boolean checkError() {
111         try {
112             out.flush();
113         } catch( IOException e ) {
114             hadError=true;
115         }
116         return hadError;
117     }
118 
119     public void visit( XSSchemaSet s ) {
120         Iterator itr =  s.iterateSchema();
121         while(itr.hasNext()) {
122             schema((XSSchema)itr.next());
123             println();
124         }
125     }
126 
127     public void schema( XSSchema s ) {
128 
129         // QUICK HACK: don't print the built-in components
130         if(s.getTargetNamespace().equals(Const.schemaNamespace))
131             return;
132 
133         println(MessageFormat.format("<schema targetNamespace=\"{0}\">", s.getTargetNamespace()));
134         indent++;
135 
136         Iterator itr;
137 
138         itr = s.iterateAttGroupDecls();
139         while(itr.hasNext())
140             attGroupDecl( (XSAttGroupDecl)itr.next() );
141 
142         itr = s.iterateAttributeDecls();
143         while(itr.hasNext())
144             attributeDecl( (XSAttributeDecl)itr.next() );
145 
146         itr = s.iterateComplexTypes();
147         while(itr.hasNext())
148             complexType( (XSComplexType)itr.next() );
149 
150         itr = s.iterateElementDecls();
151         while(itr.hasNext())
152             elementDecl( (XSElementDecl)itr.next() );
153 
154         itr = s.iterateModelGroupDecls();
155         while(itr.hasNext())
156             modelGroupDecl( (XSModelGroupDecl)itr.next() );
157 
158         itr = s.iterateSimpleTypes();
159         while(itr.hasNext())
160             simpleType( (XSSimpleType)itr.next() );
161 
162         indent--;
163         println("</schema>");
164     }
165 
166     public void attGroupDecl( XSAttGroupDecl decl ) {
167         Iterator itr;
168 
169         println(MessageFormat.format("<attGroup name=\"{0}\">", decl.getName()));
170         indent++;
171 
172         // TODO: wildcard
173 
174         itr = decl.iterateAttGroups();
175         while(itr.hasNext())
176             dumpRef( (XSAttGroupDecl)itr.next() );
177 
178         itr = decl.iterateDeclaredAttributeUses();
179         while(itr.hasNext())
180             attributeUse( (XSAttributeUse)itr.next() );
181 
182         indent--;
183         println("</attGroup>");
184     }
185 
186     public void dumpRef( XSAttGroupDecl decl ) {
187         println(MessageFormat.format("<attGroup ref=\"'{'{0}'}'{1}\"/>", decl.getTargetNamespace(), decl.getName()));
188     }
189 
190     public void attributeUse( XSAttributeUse use ) {
191         XSAttributeDecl decl = use.getDecl();
192 
193         String additionalAtts="";
194 
195         if(use.isRequired())
196             additionalAtts += " use=\"required\"";
197         if(use.getFixedValue()!=null && use.getDecl().getFixedValue()==null)
198             additionalAtts += " fixed=\""+use.getFixedValue()+'\"';
199         if(use.getDefaultValue()!=null && use.getDecl().getDefaultValue()==null)
200             additionalAtts += " default=\""+use.getDefaultValue()+'\"';
201 
202         if(decl.isLocal()) {
203             // this is anonymous attribute use
204             dump(decl,additionalAtts);
205         } else {
206             // reference to a global one
207             println(MessageFormat.format("<attribute ref=\"'{'{0}'}'{1}{2}\"/>",
208                 decl.getTargetNamespace(), decl.getName(), additionalAtts));
209         }
210     }
211 
212     public void attributeDecl( XSAttributeDecl decl ) {
213         dump(decl,"");
214     }
215 
216     private void dump( XSAttributeDecl decl, String additionalAtts ) {
217         XSSimpleType type=decl.getType();
218 
219         println(MessageFormat.format("<attribute name=\"{0}\"{1}{2}{3}{4}{5}>",
220             decl.getName(),
221             additionalAtts,
222             type.isLocal()?"":
223                 MessageFormat.format(" type=\"'{'{0}'}'{1}\"", type.getTargetNamespace(), type.getName()),
224             decl.getFixedValue()==null ?
225                 "":" fixed=\""+decl.getFixedValue()+'\"',
226             decl.getDefaultValue()==null ?
227                 "":" default=\""+decl.getDefaultValue()+'\"',
228             type.isLocal()?"":" /"));
229 
230         if(type.isLocal()) {
231             indent++;
232             simpleType(type);
233             indent--;
234             println("</attribute>");
235         }
236     }
237 
238     public void simpleType( XSSimpleType type ) {
239         println(MessageFormat.format("<simpleType{0}>", type.isLocal()?"":" name=\""+type.getName()+'\"'));
240         indent++;
241 
242         type.visit((XSSimpleTypeVisitor)this);
243 
244         indent--;
245         println("</simpleType>");
246     }
247 
248     public void listSimpleType( XSListSimpleType type ) {
249         XSSimpleType itemType = type.getItemType();
250 
251         if(itemType.isLocal()) {
252             println("<list>");
253             indent++;
254             simpleType(itemType);
255             indent--;
256             println("</list>");
257         } else {
258             // global type
259             println(MessageFormat.format("<list itemType=\"'{'{0}'}'{1}\" />",
260                 itemType.getTargetNamespace(), itemType.getName()));
261         }
262     }
263 
264     public void unionSimpleType( XSUnionSimpleType type ) {
265         final int len = type.getMemberSize();
266         StringBuffer ref = new StringBuffer();
267 
268         for( int i=0; i<len; i++ ) {
269             XSSimpleType member = type.getMember(i);
270             if(member.isGlobal())
271                 ref.append(MessageFormat.format(" '{'{0}'}'{1}", member.getTargetNamespace(),member.getName()));
272         }
273 
274         if(ref.length()==0)
275             println("<union>");
276         else
277             println("<union memberTypes=\""+ref+"\">");
278         indent++;
279 
280         for( int i=0; i<len; i++ ) {
281             XSSimpleType member = type.getMember(i);
282             if(member.isLocal())
283                 simpleType(member);
284         }
285         indent--;
286         println("</union>");
287     }
288 
289     public void restrictionSimpleType( XSRestrictionSimpleType type ) {
290 
291         if(type.getBaseType()==null) {
292             // don't print anySimpleType
293             if(!type.getName().equals("anySimpleType"))
294                 throw new InternalError();
295             if(!Const.schemaNamespace.equals(type.getTargetNamespace()))
296                 throw new InternalError();
297             return;
298         }
299 
300         XSSimpleType baseType = type.getSimpleBaseType();
301 
302         println(MessageFormat.format("<restriction{0}>",
303             baseType.isLocal()?"":" base=\"{"+
304             baseType.getTargetNamespace()+'}'+
305             baseType.getName()+'\"'));
306         indent++;
307 
308         if(baseType.isLocal())
309             simpleType(baseType);
310 
311         Iterator itr = type.iterateDeclaredFacets();
312         while(itr.hasNext())
313             facet( (XSFacet)itr.next() );
314 
315         indent--;
316         println("</restriction>");
317     }
318 
319     public void facet( XSFacet facet ) {
320         println(MessageFormat.format("<{0} value=\"{1}\"/>",
321             facet.getName(), facet.getValue()));
322     }
323 
324     public void notation( XSNotation notation ) {
325         println(MessageFormat.format("<notation name='\"0}\" public =\"{1}\" system=\"{2}\" />",
326             notation.getName(), notation.getPublicId(), notation.getSystemId()));
327     }
328 
329 
330 
331     public void complexType( XSComplexType type ) {
332         println(MessageFormat.format("<complexType{0}>",
333             type.isLocal()?"":" name=\""+type.getName()+'\"'));
334         indent++;
335 
336         // TODO: wildcard
337 
338         if(type.getContentType().asSimpleType()!=null) {
339             // simple content
340             println("<simpleContent>");
341             indent++;
342 
343             XSType baseType = type.getBaseType();
344 
345             if(type.getDerivationMethod()==XSType.RESTRICTION) {
346                 // restriction
347                 println(MessageFormat.format("<restriction base=\"<{0}>{1}\">",
348                     baseType.getTargetNamespace(), baseType.getName()));
349                 indent++;
350 
351                 dumpComplexTypeAttribute(type);
352 
353                 indent--;
354                 println("</restriction>");
355             } else {
356                 // extension
357                 println(MessageFormat.format("<extension base=\"<{0}>{1}\">",
358                     baseType.getTargetNamespace(), baseType.getName()));
359 
360                 // check if have redefine tag - Kirill
361                 if( type.isGlobal()
362                 && type.getTargetNamespace().equals(baseType.getTargetNamespace())
363                 && type.getName().equals(baseType.getName())) {
364                     indent++;
365                     println("<redefine>");
366                     indent++;
367                     baseType.visit(this);
368                     indent--;
369                     println("</redefine>");
370                     indent--;
371                 }
372 
373                 indent++;
374 
375                 dumpComplexTypeAttribute(type);
376 
377                 indent--;
378                 println("</extension>");
379             }
380 
381             indent--;
382             println("</simpleContent>");
383         } else {
384             // complex content
385             println("<complexContent>");
386             indent++;
387 
388             XSComplexType baseType = type.getBaseType().asComplexType();
389 
390             if(type.getDerivationMethod()==XSType.RESTRICTION) {
391                 // restriction
392                 println(MessageFormat.format("<restriction base=\"'{'{0}'}'{1}\">",
393                     baseType.getTargetNamespace(), baseType.getName()));
394                 indent++;
395 
396                 type.getContentType().visit(this);
397                 dumpComplexTypeAttribute(type);
398 
399                 indent--;
400                 println("</restriction>");
401             } else {
402                 // extension
403                 println(MessageFormat.format("<extension base=\"'{'{0}'}'{1}\">",
404                     baseType.getTargetNamespace(), baseType.getName()));
405 
406                 // check if have redefine - Kirill
407                 if( type.isGlobal()
408                 && type.getTargetNamespace().equals(baseType.getTargetNamespace())
409                 && type.getName().equals(baseType.getName())) {
410                     indent++;
411                     println("<redefine>");
412                     indent++;
413                     baseType.visit(this);
414                     indent--;
415                     println("</redefine>");
416                     indent--;
417                 }
418 
419                 indent++;
420 
421                 type.getExplicitContent().visit(this);
422                 dumpComplexTypeAttribute(type);
423 
424                 indent--;
425                 println("</extension>");
426             }
427 
428             indent--;
429             println("</complexContent>");
430         }
431 
432         indent--;
433         println("</complexType>");
434     }
435 
436     private void dumpComplexTypeAttribute( XSComplexType type ) {
437         Iterator itr;
438 
439         itr = type.iterateAttGroups();
440         while(itr.hasNext())
441             dumpRef( (XSAttGroupDecl)itr.next() );
442 
443         itr = type.iterateDeclaredAttributeUses();
444         while(itr.hasNext())
445             attributeUse( (XSAttributeUse)itr.next() );
446 
447         XSWildcard awc = type.getAttributeWildcard();
448         if(awc!=null)
449             wildcard("anyAttribute",awc,"");
450     }
451 
452     public void elementDecl( XSElementDecl decl ) {
453         elementDecl(decl,"");
454     }
455     private void elementDecl( XSElementDecl decl, String extraAtts ) {
456         XSType type = decl.getType();
457 
458         // TODO: various other attributes
459 
460         // qualified attr; Issue
461         if(decl.getForm() != null) {
462             extraAtts += " form=\"" + (decl.getForm() ? "qualified" : "unqualified" ) + "\"";
463         }
464 
465         println(MessageFormat.format("<element name=\"{0}\"{1}{2}{3}>",
466             decl.getName(),
467             type.isLocal()?"":" type=\"{"+
468             type.getTargetNamespace()+'}'+
469             type.getName()+'\"',
470             extraAtts,
471             type.isLocal()?"":"/"));
472 
473         if(type.isLocal()) {
474             indent++;
475 
476             if(type.isLocal())  type.visit(this);
477 
478             indent--;
479             println("</element>");
480         }
481     }
482 
483     public void modelGroupDecl( XSModelGroupDecl decl ) {
484         println(MessageFormat.format("<group name=\"{0}\">", decl.getName()));
485         indent++;
486 
487         modelGroup(decl.getModelGroup());
488 
489         indent--;
490         println("</group>");
491     }
492 
493     public void modelGroup( XSModelGroup group ) {
494         modelGroup(group,"");
495     }
496     private void modelGroup( XSModelGroup group, String extraAtts ) {
497         println(MessageFormat.format("<{0}{1}>", group.getCompositor(), extraAtts));
498         indent++;
499 
500         final int len = group.getSize();
501         for( int i=0; i<len; i++ )
502             particle(group.getChild(i));
503 
504         indent--;
505         println(MessageFormat.format("</{0}>", group.getCompositor()));
506     }
507 
508     public void particle( XSParticle part ) {
509         BigInteger i;
510 
511         StringBuilder buf = new StringBuilder();
512 
513         i = part.getMaxOccurs();
514         if(i.equals(BigInteger.valueOf(XSParticle.UNBOUNDED)))
515             buf.append(" maxOccurs=\"unbounded\"");
516         else if(!i.equals(BigInteger.ONE))
517             buf.append(" maxOccurs=\"").append(i).append('\"');
518 
519         i = part.getMinOccurs();
520         if(!i.equals(BigInteger.ONE))
521             buf.append(" minOccurs=\"").append(i).append('\"');
522 
523         final String extraAtts = buf.toString();
524 
525         part.getTerm().visit(new XSTermVisitor(){
526             public void elementDecl( XSElementDecl decl ) {
527                 if(decl.isLocal())
528                     SchemaWriter.this.elementDecl(decl,extraAtts);
529                 else {
530                     // reference
531                     println(MessageFormat.format("<element ref=\"'{'{0}'}'{1}\"{2}/>",
532                         decl.getTargetNamespace(),
533                         decl.getName(),
534                         extraAtts));
535                 }
536             }
537             public void modelGroupDecl( XSModelGroupDecl decl ) {
538                 // reference
539                 println(MessageFormat.format("<group ref=\"'{'{0}'}'{1}\"{2}/>",
540                     decl.getTargetNamespace(),
541                     decl.getName(),
542                     extraAtts));
543             }
544             public void modelGroup( XSModelGroup group ) {
545                 SchemaWriter.this.modelGroup(group,extraAtts);
546             }
547             public void wildcard( XSWildcard wc ) {
548                 SchemaWriter.this.wildcard("any",wc,extraAtts);
549             }
550         });
551     }
552 
553     public void wildcard( XSWildcard wc ) {
554         wildcard("any",wc,"");
555     }
556 
557     private void wildcard( String tagName, XSWildcard wc, String extraAtts ) {
558         final String proessContents;
559         switch(wc.getMode()) {
560         case XSWildcard.LAX:
561             proessContents = " processContents='lax'";break;
562         case XSWildcard.STRTICT:
563             proessContents = "";break;
564         case XSWildcard.SKIP:
565             proessContents = " processContents='skip'";break;
566         default:
567             throw new AssertionError();
568         }
569 
570         println(MessageFormat.format("<{0}{1}{2}{3}/>",tagName, proessContents, wc.apply(WILDCARD_NS), extraAtts));
571     }
572 
573     private static final XSWildcardFunction<String> WILDCARD_NS = new XSWildcardFunction<String>() {
574         public String any(Any wc) {
575             return ""; // default
576         }
577 
578         public String other(Other wc) {
579             return " namespace='##other'";
580         }
581 
582         public String union(Union wc) {
583             StringBuilder buf = new StringBuilder(" namespace='");
584             boolean first = true;
585             for (String s : wc.getNamespaces()) {
586                 if(first)   first=false;
587                 else        buf.append(' ');
588                 buf.append(s);
589             }
590             return buf.append('\'').toString();
591         }
592     };
593 
594     public void annotation( XSAnnotation ann ) {
595         // TODO: it would be nice even if we just put <xs:documentation>
596     }
597 
598     public void identityConstraint(XSIdentityConstraint decl) {
599         // TODO
600     }
601 
602     public void xpath(XSXPath xp) {
603         // TODO
604     }
605 
606     public void empty( XSContentType t ) {}
607 }