/* * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.xml.internal.xsom.impl.util; import com.sun.xml.internal.xsom.XSAnnotation; import com.sun.xml.internal.xsom.XSAttGroupDecl; import com.sun.xml.internal.xsom.XSAttributeDecl; import com.sun.xml.internal.xsom.XSAttributeUse; import com.sun.xml.internal.xsom.XSComplexType; import com.sun.xml.internal.xsom.XSContentType; import com.sun.xml.internal.xsom.XSElementDecl; import com.sun.xml.internal.xsom.XSFacet; import com.sun.xml.internal.xsom.XSIdentityConstraint; import com.sun.xml.internal.xsom.XSListSimpleType; import com.sun.xml.internal.xsom.XSModelGroup; import com.sun.xml.internal.xsom.XSModelGroupDecl; import com.sun.xml.internal.xsom.XSNotation; import com.sun.xml.internal.xsom.XSParticle; import com.sun.xml.internal.xsom.XSRestrictionSimpleType; import com.sun.xml.internal.xsom.XSSchema; import com.sun.xml.internal.xsom.XSSchemaSet; import com.sun.xml.internal.xsom.XSSimpleType; import com.sun.xml.internal.xsom.XSType; import com.sun.xml.internal.xsom.XSUnionSimpleType; import com.sun.xml.internal.xsom.XSWildcard; import com.sun.xml.internal.xsom.XSXPath; import com.sun.xml.internal.xsom.impl.Const; import com.sun.xml.internal.xsom.visitor.XSSimpleTypeVisitor; import com.sun.xml.internal.xsom.visitor.XSTermVisitor; import com.sun.xml.internal.xsom.visitor.XSVisitor; import java.io.IOException; import java.io.Writer; import java.text.MessageFormat; import java.util.Iterator; /** * Generates approximated XML Schema representation from * a schema component. This is not intended to be a fully-fledged * round-trippable schema writer. * * <h2>Usage of this class</h2> * <ol> * <li>Create a new instance with whatever Writer * you'd like to send the output to. * <li>Call one of the overloaded dump methods. * You can repeat this process as many times as you want. * </ol> * * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) * @author Kirill Grouchnikov (kirillcool@yahoo.com) */ public class SchemaWriter implements XSVisitor, XSSimpleTypeVisitor { public SchemaWriter( Writer _out ) { this.out=_out; } /** output is sent to this object. */ private final Writer out; /** indentation. */ private int indent; private void println(String s) { try { for( int i=0; i<indent; i++) out.write(" "); out.write(s); out.write('\n'); // flush stream to make the output appear immediately out.flush(); } catch( IOException e ) { // ignore IOException. hadError = true; } } private void println() { println(""); } /** If IOException is encountered, this flag is set to true. */ private boolean hadError =false; /** Flush the stream and check its error state. */ public boolean checkError() { try { out.flush(); } catch( IOException e ) { hadError=true; } return hadError; } public void visit( XSSchemaSet s ) { Iterator itr = s.iterateSchema(); while(itr.hasNext()) { schema((XSSchema)itr.next()); println(); } } public void schema( XSSchema s ) { // QUICK HACK: don't print the built-in components if(s.getTargetNamespace().equals(Const.schemaNamespace)) return; println(MessageFormat.format("<schema targetNamespace=\"{0}\">", new Object[]{ s.getTargetNamespace(), })); indent++; Iterator itr; itr = s.iterateAttGroupDecls(); while(itr.hasNext()) attGroupDecl( (XSAttGroupDecl)itr.next() ); itr = s.iterateAttributeDecls(); while(itr.hasNext()) attributeDecl( (XSAttributeDecl)itr.next() ); itr = s.iterateComplexTypes(); while(itr.hasNext()) complexType( (XSComplexType)itr.next() ); itr = s.iterateElementDecls(); while(itr.hasNext()) elementDecl( (XSElementDecl)itr.next() ); itr = s.iterateModelGroupDecls(); while(itr.hasNext()) modelGroupDecl( (XSModelGroupDecl)itr.next() ); itr = s.iterateSimpleTypes(); while(itr.hasNext()) simpleType( (XSSimpleType)itr.next() ); indent--; println("</schema>"); } public void attGroupDecl( XSAttGroupDecl decl ) { Iterator itr; println(MessageFormat.format("<attGroup name=\"{0}\">", new Object[]{ decl.getName() })); indent++; // TODO: wildcard itr = decl.iterateAttGroups(); while(itr.hasNext()) dumpRef( (XSAttGroupDecl)itr.next() ); itr = decl.iterateDeclaredAttributeUses(); while(itr.hasNext()) attributeUse( (XSAttributeUse)itr.next() ); indent--; println("</attGroup>"); } public void dumpRef( XSAttGroupDecl decl ) { println(MessageFormat.format("<attGroup ref=\"'{'{0}'}'{1}\"/>", new Object[]{ decl.getTargetNamespace(), decl.getName() })); } public void attributeUse( XSAttributeUse use ) { XSAttributeDecl decl = use.getDecl(); String additionalAtts=""; if(use.isRequired()) additionalAtts += " use=\"required\""; if(use.getFixedValue()!=null && use.getDecl().getFixedValue()==null) additionalAtts += " fixed=\""+use.getFixedValue()+'\"'; if(use.getDefaultValue()!=null && use.getDecl().getDefaultValue()==null) additionalAtts += " default=\""+use.getDefaultValue()+'\"'; if(decl.isLocal()) { // this is anonymous attribute use dump(decl,additionalAtts); } else { // reference to a global one println(MessageFormat.format("<attribute ref=\"'{'{0}'}'{1}{2}\"/>", new Object[]{ decl.getTargetNamespace(), decl.getName(), additionalAtts })); } } public void attributeDecl( XSAttributeDecl decl ) { dump(decl,""); } private void dump( XSAttributeDecl decl, String additionalAtts ) { XSSimpleType type=decl.getType(); println(MessageFormat.format("<attribute name=\"{0}\"{1}{2}{3}{4}{5}>", new Object[]{ decl.getName(), additionalAtts, type.isLocal()?"": MessageFormat.format(" type=\"'{'{0}'}'{1}\"", new Object[]{ type.getTargetNamespace(), type.getName() }), decl.getFixedValue()==null ? "":" fixed=\""+decl.getFixedValue()+'\"', decl.getDefaultValue()==null ? "":" default=\""+decl.getDefaultValue()+'\"', type.isLocal()?"":" /" })); if(type.isLocal()) { indent++; simpleType(type); indent--; println("</attribute>"); } } public void simpleType( XSSimpleType type ) { println(MessageFormat.format("<simpleType{0}>", new Object[]{ type.isLocal()?"":" name=\""+type.getName()+'\"' })); indent++; type.visit((XSSimpleTypeVisitor)this); indent--; println("</simpleType>"); } public void listSimpleType( XSListSimpleType type ) { XSSimpleType itemType = type.getItemType(); if(itemType.isLocal()) { println("<list>"); indent++; simpleType(itemType); indent--; println("</list>"); } else { // global type println(MessageFormat.format("<list itemType=\"'{'{0}'}'{1}\" />", new Object[]{ itemType.getTargetNamespace(), itemType.getName() })); } } public void unionSimpleType( XSUnionSimpleType type ) { final int len = type.getMemberSize(); StringBuffer ref = new StringBuffer(); for( int i=0; i<len; i++ ) { XSSimpleType member = type.getMember(i); if(member.isGlobal()) ref.append(MessageFormat.format(" '{'{0}'}'{1}", new Object[]{member.getTargetNamespace(),member.getName()})); } if(ref.length()==0) println("<union>"); else println("<union memberTypes=\""+ref+"\">"); indent++; for( int i=0; i<len; i++ ) { XSSimpleType member = type.getMember(i); if(member.isLocal()) simpleType(member); } indent--; println("</union>"); } public void restrictionSimpleType( XSRestrictionSimpleType type ) { if(type.getBaseType()==null) { // don't print anySimpleType if(!type.getName().equals("anySimpleType")) throw new InternalError(); if(!Const.schemaNamespace.equals(type.getTargetNamespace())) throw new InternalError(); return; } XSSimpleType baseType = type.getSimpleBaseType(); println(MessageFormat.format("<restriction{0}>", new Object[]{ baseType.isLocal()?"":" base=\"{"+ baseType.getTargetNamespace()+'}'+ baseType.getName()+'\"' })); indent++; if(baseType.isLocal()) simpleType(baseType); Iterator itr = type.iterateDeclaredFacets(); while(itr.hasNext()) facet( (XSFacet)itr.next() ); indent--; println("</restriction>"); } public void facet( XSFacet facet ) { println(MessageFormat.format("<{0} value=\"{1}\"/>", new Object[]{ facet.getName(), facet.getValue(), })); } public void notation( XSNotation notation ) { println(MessageFormat.format("<notation name='\"0}\" public =\"{1}\" system=\"{2}\" />", new Object[] { notation.getName(), notation.getPublicId(), notation.getSystemId() } )); } public void complexType( XSComplexType type ) { println(MessageFormat.format("<complexType{0}>", new Object[]{ type.isLocal()?"":" name=\""+type.getName()+'\"' })); indent++; // TODO: wildcard if(type.getContentType().asSimpleType()!=null) { // simple content println("<simpleContent>"); indent++; XSType baseType = type.getBaseType(); if(type.getDerivationMethod()==XSType.RESTRICTION) { // restriction println(MessageFormat.format("<restriction base=\"<{0}>{1}\">", new Object[]{ baseType.getTargetNamespace(), baseType.getName() })); indent++; dumpComplexTypeAttribute(type); indent--; println("</restriction>"); } else { // extension println(MessageFormat.format("<extension base=\"<{0}>{1}\">", new Object[]{ baseType.getTargetNamespace(), baseType.getName() })); // check if have redefine tag - Kirill if( type.isGlobal() && type.getTargetNamespace().equals(baseType.getTargetNamespace()) && type.getName().equals(baseType.getName())) { indent++; println("<redefine>"); indent++; baseType.visit(this); indent--; println("</redefine>"); indent--; } indent++; dumpComplexTypeAttribute(type); indent--; println("</extension>"); } indent--; println("</simpleContent>"); } else { // complex content println("<complexContent>"); indent++; XSComplexType baseType = type.getBaseType().asComplexType(); if(type.getDerivationMethod()==XSType.RESTRICTION) { // restriction println(MessageFormat.format("<restriction base=\"'{'{0}'}'{1}\">", new Object[]{ baseType.getTargetNamespace(), baseType.getName() })); indent++; type.getContentType().visit(this); dumpComplexTypeAttribute(type); indent--; println("</restriction>"); } else { // extension println(MessageFormat.format("<extension base=\"'{'{0}'}'{1}\">", new Object[]{ baseType.getTargetNamespace(), baseType.getName() })); // check if have redefine - Kirill if( type.isGlobal() && type.getTargetNamespace().equals(baseType.getTargetNamespace()) && type.getName().equals(baseType.getName())) { indent++; println("<redefine>"); indent++; baseType.visit(this); indent--; println("</redefine>"); indent--; } indent++; type.getExplicitContent().visit(this); dumpComplexTypeAttribute(type); indent--; println("</extension>"); } indent--; println("</complexContent>"); } indent--; println("</complexType>"); } private void dumpComplexTypeAttribute( XSComplexType type ) { Iterator itr; itr = type.iterateAttGroups(); while(itr.hasNext()) dumpRef( (XSAttGroupDecl)itr.next() ); itr = type.iterateDeclaredAttributeUses(); while(itr.hasNext()) attributeUse( (XSAttributeUse)itr.next() ); } public void elementDecl( XSElementDecl decl ) { elementDecl(decl,""); } private void elementDecl( XSElementDecl decl, String extraAtts ) { XSType type = decl.getType(); // TODO: various other attributes println(MessageFormat.format("<element name=\"{0}\"{1}{2}{3}>", new Object[]{ decl.getName(), type.isLocal()?"":" type=\"{"+ type.getTargetNamespace()+'}'+ type.getName()+'\"', extraAtts, type.isLocal()?"":"/" })); if(type.isLocal()) { indent++; if(type.isLocal()) type.visit(this); indent--; println("</element>"); } } public void modelGroupDecl( XSModelGroupDecl decl ) { println(MessageFormat.format("<group name=\"{0}\">", new Object[]{ decl.getName() })); indent++; modelGroup(decl.getModelGroup()); indent--; println("</group>"); } public void modelGroup( XSModelGroup group ) { modelGroup(group,""); } private void modelGroup( XSModelGroup group, String extraAtts ) { println(MessageFormat.format("<{0}{1}>", new Object[]{ group.getCompositor(), extraAtts })); indent++; final int len = group.getSize(); for( int i=0; i<len; i++ ) particle(group.getChild(i)); indent--; println(MessageFormat.format("</{0}>", new Object[]{ group.getCompositor() })); } public void particle( XSParticle part ) { int i; StringBuffer buf = new StringBuffer(); i = part.getMaxOccurs(); if(i==XSParticle.UNBOUNDED) buf.append(" maxOccurs=\"unbounded\""); else if(i!=1) buf.append(" maxOccurs=\""+i+'\"'); i = part.getMinOccurs(); if(i!=1) buf.append(" minOccurs=\""+i+'\"'); final String extraAtts = buf.toString(); part.getTerm().visit(new XSTermVisitor(){ public void elementDecl( XSElementDecl decl ) { if(decl.isLocal()) SchemaWriter.this.elementDecl(decl,extraAtts); else { // reference println(MessageFormat.format("<element ref=\"'{'{0}'}'{1}\"{2}/>", new Object[]{ decl.getTargetNamespace(), decl.getName(), extraAtts })); } } public void modelGroupDecl( XSModelGroupDecl decl ) { // reference println(MessageFormat.format("<group ref=\"'{'{0}'}'{1}\"{2}/>", new Object[]{ decl.getTargetNamespace(), decl.getName(), extraAtts })); } public void modelGroup( XSModelGroup group ) { SchemaWriter.this.modelGroup(group,extraAtts); } public void wildcard( XSWildcard wc ) { SchemaWriter.this.wildcard(wc,extraAtts); } }); } public void wildcard( XSWildcard wc ) { wildcard(wc,""); } private void wildcard( XSWildcard wc, String extraAtts ) { // TODO println(MessageFormat.format("<any/>", new Object[]{extraAtts})); } public void annotation( XSAnnotation ann ) { // TODO: it would be nice even if we just put <xs:documentation> } public void identityConstraint(XSIdentityConstraint decl) { // TODO } public void xpath(XSXPath xp) { // TODO } public void empty( XSContentType t ) {} }