001 package xmlBeans.schema;
002
003 import com.bea.xbean.tool.CommandLine;
004 import com.bea.xml.XmlObject;
005 import com.bea.xml.XmlOptions;
006 import com.bea.xml.XmlBeans;
007 import com.bea.xml.XmlException;
008 import com.bea.xml.SchemaTypeSystem;
009 import com.bea.xml.SchemaType;
010
011 import java.util.Collections;
012 import java.util.List;
013 import java.util.ArrayList;
014 import java.util.Collection;
015 import java.util.Iterator;
016 import java.util.HashMap;
017 import java.util.Map;
018 import java.util.Arrays;
019 import java.io.File;
020
021 /**
022 * Provides a way to print a hierarchical list of the XML schema types
023 * corresponding to Java types that result from compiling the schema.
024 * The unique "signatures" used to represent the schema types in the hierarchy
025 * are built from several characteristics, including:
026 * <ul>
027 * <li>The type's position in the schema (for example, is it global or local?).</li>
028 * <li>Whether the type is a datatype (complex or simple) or an element or attribute.</li>
029 * <li>Whether the type is derived by restriction, union, and so on.</li>
030 * </ol>
031 * There isn't yet a public standard for such signatures, but the style used here
032 * is useful for seeing the hierarchy. Keep in mind that if a signature standard does
033 * become adopted, that standard will likely be used by the SchemaType.toString()
034 * method. See the WebLogic Workshop documentation for a more complete description
035 * of the signature conventions used here.
036 * <br/><br/>
037 * This class is designed so that it may be used from either another
038 * component (such as a web service) or from the command line.
039 */
040 public class TypeHierarchyPrinter
041 {
042 /**
043 * Prints a hierarchical list of XML schema types represented by the
044 * specified <em>typeSystem</em>.
045 */
046 public static String printHierarchy(SchemaTypeSystem typeSystem) throws Exception
047 {
048 // A StringBuffer in which to build the response hierarchy.
049 StringBuffer response = new StringBuffer();
050
051 // A map for base SchemaType -> Collection of directly derived types.
052 Map childTypes = new HashMap();
053
054 // Traverse the type containment tree breadthfirst.
055 List allSeenTypes = new ArrayList();
056 // Document types are special types that hold the globally defined elements.
057 allSeenTypes.addAll(Arrays.asList(typeSystem.documentTypes()));
058 // Attribute types are special types that hold the globally defined attributes.
059 allSeenTypes.addAll(Arrays.asList(typeSystem.attributeTypes()));
060 /*
061 * Global types are globally defined schema types -- such as complex or
062 * simple types defined just beneath the schema's root in the hierarchy.
063 */
064 allSeenTypes.addAll(Arrays.asList(typeSystem.globalTypes()));
065
066 /**
067 * Loop through the types to examine,
068 */
069 for (int i = 0; i < allSeenTypes.size(); i++)
070 {
071 /**
072 * A SchemaType object represents a compiled schema type. This
073 * includes all three of the kinds added to allSeenTypes above.
074 */
075 SchemaType sType = (SchemaType)allSeenTypes.get(i);
076
077 /*
078 * Recurse through the nested anonymous types as well
079 * (comment this line to skip nested types). Anonymous types
080 * are defined inside other types.
081 */
082 allSeenTypes.addAll(Arrays.asList(sType.getAnonymousTypes()));
083
084 /**
085 * Don't examine nested document types, attribute types,
086 * or the base type of anyType.
087 */
088 if (sType.isDocumentType() || sType.isAttributeType() || sType == XmlObject.type)
089 continue;
090
091 // Enter this type in the list of children of its base type.
092 Collection children = (Collection)childTypes.get(sType.getBaseType());
093 if (children == null)
094 {
095 children = new ArrayList();
096 childTypes.put(sType.getBaseType(), children);
097
098 /**
099 * The first time a built-in type is seen add it, too,
100 * to get a complete tree up to anyType, the root. A built-in type
101 * is a schema type defined by the schema specification,
102 * such as xs:string, xs:int, xs:anyType, and so on.
103 */
104 if (sType.getBaseType().isBuiltinType())
105 allSeenTypes.add(sType.getBaseType());
106 }
107 children.add(sType);
108 }
109
110 /**
111 * Print the hierarchy tree.
112 */
113 List typesToPrint = new ArrayList();
114
115 // Add anyType, from which all others inherit.
116 typesToPrint.add(XmlObject.type);
117
118 // Create a buffer to hold the indentation spaces.
119 StringBuffer spaces = new StringBuffer();
120
121 /**
122 * Loop through the list of types, adding the hierarchy branch
123 * symbols and type "signatures".
124 */
125 while (!typesToPrint.isEmpty())
126 {
127 SchemaType sType = (SchemaType)typesToPrint.remove(typesToPrint.size() - 1);
128 if (sType == null)
129 spaces.setLength(Math.max(0, spaces.length() - 2));
130 else
131 {
132 /**
133 * The SchemaType.toString() method returns a String containing
134 * the schema type "signature".
135 */
136 response.append(spaces + "+-" + sType.toString() + "\n");
137 Collection children = (Collection)childTypes.get(sType);
138 if (children != null && children.size() > 0)
139 {
140 spaces.append(typesToPrint.size() == 0 || typesToPrint.get(typesToPrint.size() - 1) == null ? " " : "| ");
141 typesToPrint.add(null);
142 typesToPrint.addAll(children);
143 }
144 }
145 }
146 return response.toString();
147 }
148
149 /**
150 * Creates a schema type system from an array of XSD files. This method
151 * is called when this class is used from the command line.
152 */
153 public static SchemaTypeSystem typeSystemFromFiles(File[] schemaFiles)
154 {
155 /*
156 * A SchemaTypeSystem object will hold the type system created by
157 * compiling the schema types.
158 */
159 SchemaTypeSystem typeSystem = null;
160
161 List sdocs = new ArrayList();
162
163 /**
164 * Loop through the File array, parsing the contents of each schema
165 * file into an XmlObject instance. These objects will be passed to
166 * a method that compiles the schemas.
167 */
168 for (int i = 0; i < schemaFiles.length; i++)
169 {
170 try
171 {
172 sdocs.add(XmlObject.Factory.parse(
173 schemaFiles[i], (new XmlOptions()).setLoadLineNumbers()));
174 }
175 catch (Exception e)
176 {
177 System.err.println( schemaFiles[i] + " not loadable: " + e );
178 }
179 }
180 XmlObject[] schemas = (XmlObject[])sdocs.toArray(new XmlObject[0]);
181
182 /**
183 * Compile each schema. Use a Collection object to collect any
184 * errors that arise during compilation.
185 */
186 Collection compErrors = new ArrayList();
187 try
188 {
189 /**
190 * Compile the schemas, specifying also the built-in type system
191 * to consult for already-compiled schema types which
192 * may be linked while processing the given schemas.
193 */
194 typeSystem = XmlBeans.compileXsd(schemas,
195 XmlBeans.getBuiltinTypeSystem(),
196 new XmlOptions().setErrorListener(compErrors).setCompileDownloadUrls());
197 }
198 catch (XmlException e)
199 {
200 System.out.println("Schema invalid");
201 for (Iterator i = compErrors.iterator(); i.hasNext(); )
202 System.out.println(i.next());
203 }
204 return typeSystem;
205 }
206
207 /**
208 * A main method so that this class may be used from the command
209 * line.
210 */
211 public static void main(String[] args) throws Exception
212 {
213 CommandLine cl = new CommandLine(args, Collections.EMPTY_SET);
214 File[] schemaFiles = cl.getFiles();
215
216 SchemaTypeSystem typeSystem = typeSystemFromFiles(schemaFiles);
217 String hierarchy = printHierarchy(typeSystem);
218 System.out.println(hierarchy);
219 }
220
221 }
|