001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.spring.generator;
018
019 import java.io.File;
020 import java.io.FileWriter;
021 import java.io.IOException;
022 import java.io.PrintWriter;
023 import java.util.Iterator;
024 import java.util.List;
025
026 /**
027 * @author Dain Sundstrom
028 * @version $Id$
029 * @since 1.0
030 */
031 public class XsdGenerator implements GeneratorPlugin {
032 private final File destFile;
033 private LogFacade log;
034
035 public XsdGenerator(File destFile) {
036 this.destFile = destFile;
037 }
038
039 public void generate(NamespaceMapping namespaceMapping) throws IOException {
040 // TODO can only handle 1 schema document so far...
041 File file = destFile;
042 log.log("Generating XSD file: " + file + " for namespace: " + namespaceMapping.getNamespace());
043 PrintWriter out = new PrintWriter(new FileWriter(file));
044 try {
045 generateSchema(out, namespaceMapping);
046 } finally {
047 out.close();
048 }
049 }
050
051 private void generateSchema(PrintWriter out, NamespaceMapping namespaceMapping) {
052 out.println("<?xml version='1.0'?>");
053 out.println("<!-- NOTE: this file is autogenerated by Apache XBean -->");
054 out.println();
055 out.println("<xs:schema elementFormDefault='qualified'");
056 out.println(" targetNamespace='" + namespaceMapping.getNamespace() + "'");
057 out.println(" xmlns:xs='http://www.w3.org/2001/XMLSchema'");
058 out.println(" xmlns:tns='" + namespaceMapping.getNamespace() + "'>");
059
060 for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) {
061 ElementMapping element = (ElementMapping) iter.next();
062 generateElementMapping(out, namespaceMapping, element);
063 }
064
065 out.println();
066 out.println("</xs:schema>");
067 }
068
069 private void generateElementMapping(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element) {
070 out.println();
071 out.println(" <!-- element for type: " + element.getClassName() + " -->");
072
073 String localName = element.getElementName();
074
075 out.println(" <xs:element name='" + localName + "'>");
076
077 if (!isEmptyString(element.getDescription())) {
078 out.println(" <xs:annotation>");
079 out.println(" <xs:documentation><![CDATA[");
080 out.println(" " + element.getDescription());
081 out.println(" ]]></xs:documentation>");
082 out.println(" </xs:annotation>");
083 }
084
085 out.println(" <xs:complexType>");
086
087 int complexCount = 0;
088 for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) {
089 AttributeMapping attributeMapping = (AttributeMapping) iterator.next();
090 if (!namespaceMapping.isSimpleType(attributeMapping.getType())) {
091 complexCount++;
092 }
093 }
094 if (complexCount > 0) {
095 out.println(" <xs:sequence>");
096 for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) {
097 AttributeMapping attributeMapping = (AttributeMapping) iterator.next();
098 if (!namespaceMapping.isSimpleType(attributeMapping.getType())) {
099 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 if (!isEmptyString(attributeMapping.getDescription())) {
146 out.println(" <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='" + Utils.getXsdType(attributeMapping.getType()) + "'>");
147 out.println(" <xs:annotation>");
148 out.println(" <xs:documentation><![CDATA[");
149 out.println(" " + attributeMapping.getDescription());
150 out.println(" ]]></xs:documentation>");
151 out.println(" </xs:annotation>");
152 out.println(" </xs:attribute>");
153 } else {
154 out.println(" <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='" + Utils.getXsdType(attributeMapping.getType()) + "'/>");
155 }
156 }
157
158 private void generateElementMappingComplexPropertyAsRef(PrintWriter out, AttributeMapping attributeMapping) {
159 if (!isEmptyString(attributeMapping.getDescription())) {
160 out.println(" <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='xs:string'>");
161 out.println(" <xs:annotation>");
162 out.println(" <xs:documentation><![CDATA[");
163 out.println(" " + attributeMapping.getDescription());
164 out.println(" ]]></xs:documentation>");
165 out.println(" </xs:annotation>");
166 out.println(" </xs:attribute>");
167 } else {
168 out.println(" <xs:attribute name='" + attributeMapping.getAttributeName() + "' type='xs:string'/>");
169 }
170 }
171
172 private void generateElementMappingComplexProperty(PrintWriter out, NamespaceMapping namespaceMapping, AttributeMapping attributeMapping) {
173 Type type = attributeMapping.getType();
174 List types;
175 if (type.isCollection()) {
176 types = Utils.findImplementationsOf(namespaceMapping, type.getNestedType());
177 } else {
178 types = Utils.findImplementationsOf(namespaceMapping, type);
179 }
180 String maxOccurs = type.isCollection() || "java.util.Map".equals(type.getName()) ? "unbounded" : "1";
181
182 out.println(" <xs:element name='" + attributeMapping.getAttributeName() + "' minOccurs='0' maxOccurs='1'>");
183 if (!isEmptyString(attributeMapping.getDescription())) {
184 out.println(" <xs:annotation>");
185 out.println(" <xs:documentation><![CDATA[");
186 out.println(" " + attributeMapping.getDescription());
187 out.println(" ]]></xs:documentation>");
188 out.println(" </xs:annotation>");
189 }
190 out.println(" <xs:complexType>");
191 if (types.isEmpty()) {
192 out.println(" <xs:sequence minOccurs='0' maxOccurs='" + maxOccurs + "'><xs:any namespace='##other'/></xs:sequence>");
193 } else {
194 out.println(" <xs:choice minOccurs='0' maxOccurs='" + maxOccurs + "'>");
195 for (Iterator iterator = types.iterator(); iterator.hasNext();) {
196 ElementMapping element = (ElementMapping) iterator.next();
197 out.println(" <xs:element ref='tns:" + element.getElementName() + "'/>");
198 }
199 out.println(" <xs:any namespace='##other'/>");
200 out.println(" </xs:choice>");
201 }
202 out.println(" </xs:complexType>");
203 out.println(" </xs:element>");
204 }
205
206 public LogFacade getLog() {
207 return log;
208 }
209
210 public void setLog(LogFacade log) {
211 this.log = log;
212 }
213 }