View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.xbean.spring.generator;
18  
19  import java.io.File;
20  import java.io.FileWriter;
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  /**
27   * @author Dain Sundstrom
28   * @version $Id$
29   * @since 1.0
30   */
31  public class XsdGenerator implements GeneratorPlugin {
32      private final File destFile;
33      private LogFacade log;
34  
35      public XsdGenerator(File destFile) {
36          this.destFile = destFile;
37      }
38  
39      public void generate(NamespaceMapping namespaceMapping) throws IOException {
40          // TODO can only handle 1 schema document so far...
41          File file = destFile;
42          log.log("Generating XSD file: " + file + " for namespace: " + namespaceMapping.getNamespace());
43          PrintWriter out = new PrintWriter(new FileWriter(file));
44          try {
45              generateSchema(out, namespaceMapping);
46          } finally {
47              out.close();
48          }
49      }
50  
51      public void generateSchema(PrintWriter out, NamespaceMapping namespaceMapping) {
52          out.println("<?xml version='1.0'?>");
53          out.println("<!-- NOTE: this file is autogenerated by Apache XBean -->");
54          out.println();
55          out.println("<xs:schema elementFormDefault='qualified'");
56          out.println("           targetNamespace='" + namespaceMapping.getNamespace() + "'");
57          out.println("           xmlns:xs='http://www.w3.org/2001/XMLSchema'");
58          out.println("           xmlns:tns='" + namespaceMapping.getNamespace() + "'>");
59  
60          for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) {
61              ElementMapping element = (ElementMapping) iter.next();
62              generateElementMapping(out, namespaceMapping, element);
63          }
64  
65          out.println();
66          out.println("</xs:schema>");
67      }
68  
69      private void generateElementMapping(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element) {
70          out.println();
71          out.println("  <!-- element for type: " + element.getClassName() + " -->");
72  
73          String localName = element.getElementName();
74  
75          out.println("  <xs:element name='" + localName + "'>");
76  
77          if (!isEmptyString(element.getDescription())) {
78              out.println("    <xs:annotation>");
79              out.println("      <xs:documentation><![CDATA[");
80              out.println("        " + element.getDescription());
81              out.println("      ]]></xs:documentation>");
82              out.println("    </xs:annotation>");
83          }
84  
85          out.println("    <xs:complexType>");
86  
87          int complexCount = 0;
88          for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) {
89              AttributeMapping attributeMapping = (AttributeMapping) iterator.next();
90              if (!namespaceMapping.isSimpleType(attributeMapping.getType())) {
91                  complexCount++;
92              }
93          }
94          if (complexCount > 0) {
95              out.println("      <xs:sequence>");
96              for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) {
97                  AttributeMapping attributeMapping = (AttributeMapping) iterator.next();
98                  if (!namespaceMapping.isSimpleType(attributeMapping.getType())) {
99                      generateElementMappingComplexProperty(out, namespaceMapping, attributeMapping);
100                 }
101             }
102             out.println("        <xs:any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>");
103             out.println("      </xs:sequence>");
104         }
105 
106         for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) {
107             AttributeMapping attributeMapping = (AttributeMapping) iterator.next();
108             if (namespaceMapping.isSimpleType(attributeMapping.getType())) {
109                 generateElementMappingSimpleProperty(out, attributeMapping);
110             } else if (!attributeMapping.getType().isCollection()) {
111                 generateElementMappingComplexPropertyAsRef(out, attributeMapping);
112             }
113         }
114         generateIDAttributeMapping(out, namespaceMapping, element);
115 
116         out.println("      <xs:anyAttribute namespace='##other' processContents='lax'/>");
117         out.println("    </xs:complexType>");
118         out.println("  </xs:element>");
119         out.println();
120     }
121 
122     private boolean isEmptyString(String str) {
123         if (str == null) {
124             return true;
125         }
126         for (int i = 0; i < str.length(); i++) {
127             if (!Character.isWhitespace(str.charAt(i))) {
128                 return false;
129             }
130         }
131         return true;
132     }
133 
134     private void generateIDAttributeMapping(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element) {
135         for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) {
136             AttributeMapping attributeMapping = (AttributeMapping) iterator.next();
137             if ("id".equals(attributeMapping.getAttributeName())) {
138                 return;
139             }
140         }
141         out.println("      <xs:attribute name='id' type='xs:ID'/>");
142     }
143 
144     private void generateElementMappingSimpleProperty(PrintWriter out, AttributeMapping attributeMapping) {
145     	// types with property editors need to be xs:string in the schema to validate
146     	String type = attributeMapping.getPropertyEditor() != null ? 
147     			Utils.getXsdType(Type.newSimpleType(String.class.getName())) : Utils.getXsdType(attributeMapping.getType());
148         if (!isEmptyString(attributeMapping.getDescription())) {
149             out.println("      <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='" + type + "'>");
150             out.println("        <xs:annotation>");
151             out.println("          <xs:documentation><![CDATA[");
152             out.println("            " + attributeMapping.getDescription());
153             out.println("          ]]></xs:documentation>");
154             out.println("        </xs:annotation>");
155             out.println("      </xs:attribute>");
156         } else {
157             out.println("      <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='" + type + "'/>");
158         }
159     }
160 
161     private void generateElementMappingComplexPropertyAsRef(PrintWriter out, AttributeMapping attributeMapping) {
162         if (!isEmptyString(attributeMapping.getDescription())) {
163             out.println("      <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='xs:string'>");
164             out.println("        <xs:annotation>");
165             out.println("          <xs:documentation><![CDATA[");
166             out.println("            " + attributeMapping.getDescription());
167             out.println("          ]]></xs:documentation>");
168             out.println("        </xs:annotation>");
169             out.println("      </xs:attribute>");
170         } else {
171             out.println("      <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='xs:string'/>");
172         }
173     }
174 
175     private void generateElementMappingComplexProperty(PrintWriter out, NamespaceMapping namespaceMapping, AttributeMapping attributeMapping) {
176         Type type = attributeMapping.getType();
177         List types;
178         if (type.isCollection()) {
179             types = Utils.findImplementationsOf(namespaceMapping, type.getNestedType());
180         } else {
181             types = Utils.findImplementationsOf(namespaceMapping, type);
182         }
183         String maxOccurs = type.isCollection() || "java.util.Map".equals(type.getName()) ? "unbounded" : "1";
184 
185         out.println("        <xs:element name='" + attributeMapping.getAttributeName() + "' minOccurs='0' maxOccurs='1'>");
186         if (!isEmptyString(attributeMapping.getDescription())) {
187             out.println("          <xs:annotation>");
188             out.println("            <xs:documentation><![CDATA[");
189             out.println("              " + attributeMapping.getDescription());
190             out.println("            ]]></xs:documentation>");
191             out.println("          </xs:annotation>");
192         }
193         out.println("          <xs:complexType>");
194         if (types.isEmpty()) {
195             // We don't know the type because it's generic collection.  Allow folks to insert objets from any namespace
196             out.println("            <xs:sequence minOccurs='0' maxOccurs='" + maxOccurs + "'><xs:any minOccurs='0' maxOccurs='unbounded'/></xs:sequence>");
197         } else {
198             out.println("            <xs:choice minOccurs='0' maxOccurs='" + maxOccurs + "'>");
199             for (Iterator iterator = types.iterator(); iterator.hasNext();) {
200                 ElementMapping element = (ElementMapping) iterator.next();
201                 out.println("              <xs:element ref='tns:" + element.getElementName() + "'/>");
202             }
203             out.println("              <xs:any namespace='##other'/>");
204             out.println("            </xs:choice>");
205         }
206         out.println("          </xs:complexType>");
207         out.println("        </xs:element>");
208     }
209 
210     public LogFacade getLog() {
211         return log;
212     }
213 
214     public void setLog(LogFacade log) {
215         this.log = log;
216     }
217 
218 }