/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.jaxrs.ext.codegen;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Logger;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.xml.namespace.QName;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import org.apache.cxf.Bus;
import org.apache.cxf.catalog.OASISCatalogManager;
import org.apache.cxf.catalog.OASISCatalogManagerHelper;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.PackageUtils;
import org.apache.cxf.common.util.ReflectionInvokationHandler;
import org.apache.cxf.common.xmlschema.XmlSchemaConstants;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.jaxb.JAXBUtils;
import org.apache.cxf.jaxb.JAXBUtils.JCodeModel;
import org.apache.cxf.jaxb.JAXBUtils.S2JJAXBModel;
import org.apache.cxf.jaxb.JAXBUtils.SchemaCompiler;
import org.apache.cxf.jaxrs.model.wadl.WadlGenerator;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.jaxrs.utils.ResourceUtils;
import org.apache.cxf.service.model.SchemaInfo;
import org.apache.cxf.staxutils.StaxUtils;
/**
* TODO: This will need to be moved into a separate module
*/
public class SourceGenerator {
public static final String CODE_TYPE_GRAMMAR = "grammar";
public static final String CODE_TYPE_PROXY = "proxy";
public static final String CODE_TYPE_WEB = "web";
public static final String LINE_SEP_PROPERTY = "line.separator";
public static final String FILE_SEP_PROPERTY = "file.separator";
private static final Logger LOG = LogUtils.getL7dLogger(SourceGenerator.class);
private static final String DEFAULT_PACKAGE_NAME = "application";
private static final String DEFAULT_RESOURCE_NAME = "Resource";
private static final String TAB = " ";
private static final Map<String, Class<?>> HTTP_METHOD_ANNOTATIONS;
private static final Map<String, Class<?>> PARAM_ANNOTATIONS;
static {
HTTP_METHOD_ANNOTATIONS = new HashMap<String, Class<?>>();
HTTP_METHOD_ANNOTATIONS.put("get", GET.class);
HTTP_METHOD_ANNOTATIONS.put("put", PUT.class);
HTTP_METHOD_ANNOTATIONS.put("post", POST.class);
HTTP_METHOD_ANNOTATIONS.put("delete", DELETE.class);
HTTP_METHOD_ANNOTATIONS.put("head", HEAD.class);
HTTP_METHOD_ANNOTATIONS.put("options", OPTIONS.class);
PARAM_ANNOTATIONS = new HashMap<String, Class<?>>();
PARAM_ANNOTATIONS.put("template", PathParam.class);
PARAM_ANNOTATIONS.put("header", HeaderParam.class);
PARAM_ANNOTATIONS.put("query", QueryParam.class);
PARAM_ANNOTATIONS.put("matrix", MatrixParam.class);
}
private Comparator<String> importsComparator;
private boolean generateInterfaces = true;
private boolean generateImpl;
private String resourcePackageName;
private String resourceName;
private String wadlPath;
private Map<String, String> properties;
private List<String> generatedServiceClasses = new ArrayList<String>();
private List<String> generatedTypeClasses = new ArrayList<String>();
private List<InputSource> bindingFiles = Collections.emptyList();
private List<InputSource> schemaPackageFiles = Collections.emptyList();
private Map<String, String> schemaPackageMap = Collections.emptyMap();
private Bus bus;
public SourceGenerator() {
this(Collections.<String, String>emptyMap());
}
public SourceGenerator(Map<String, String> properties) {
this.properties = properties;
}
private String getClassPackageName(String wadlPackageName) {
if (resourcePackageName != null) {
return resourcePackageName;
} else if (wadlPackageName != null && wadlPackageName.length() > 0) {
return wadlPackageName;
} else {
return DEFAULT_PACKAGE_NAME;
}
}
private String getLineSep() {
String value = properties.get(LINE_SEP_PROPERTY);
return value == null ? System.getProperty(LINE_SEP_PROPERTY) : value;
}
private String getFileSep() {
String value = properties.get(FILE_SEP_PROPERTY);
return value == null ? System.getProperty(FILE_SEP_PROPERTY) : value;
}
public void generateSource(String wadl, File srcDir, String codeType) {
Application app = readWadl(wadl, wadlPath);
Set<String> typeClassNames = new HashSet<String>();
GrammarInfo gInfo = generateSchemaCodeAndInfo(app, typeClassNames, srcDir);
if (!CODE_TYPE_GRAMMAR.equals(codeType)) {
generateResourceClasses(app, gInfo, typeClassNames, srcDir);
}
}
private GrammarInfo generateSchemaCodeAndInfo(Application app, Set<String> typeClassNames,
File srcDir) {
List<SchemaInfo> schemaElements = getSchemaElements(app);
if (schemaElements != null && !schemaElements.isEmpty()) {
// generate classes from schema
JCodeModel codeModel = createCodeModel(schemaElements, typeClassNames);
if (codeModel != null) {
generateClassesFromSchema(codeModel, srcDir);
}
}
return getGrammarInfo(app.getAppElement(), schemaElements);
}
private void generateResourceClasses(Application app, GrammarInfo gInfo,
Set<String> typeClassNames, File src) {
Element appElement = app.getAppElement();
List<Element> resourcesEls = DOMUtils.getChildrenWithName(appElement,
WadlGenerator.WADL_NS, "resources");
if (resourcesEls.size() != 1) {
throw new IllegalStateException("WADL resources element is missing");
}
List<Element> resourceEls = DOMUtils.getChildrenWithName(resourcesEls.get(0),
WadlGenerator.WADL_NS, "resource");
if (resourceEls.size() == 0) {
throw new IllegalStateException("WADL has no resource elements");
}
for (int i = 0; i < resourceEls.size(); i++) {
Element resource = getResourceElement(app, resourceEls.get(i), gInfo, typeClassNames,
resourceEls.get(i).getAttribute("type"), src);
writeResourceClass(app, resource, typeClassNames, gInfo, src, true, generateInterfaces);
if (generateInterfaces && generateImpl) {
writeResourceClass(app, resource, typeClassNames, gInfo, src, true, false);
}
if (resourceName != null) {
break;
}
}
generateMainClass(resourcesEls.get(0), src);
}
//TODO: similar procedure should work for representation, method and param
// thus some of the code here will need to be moved into a sep function to be
// reused by relevant handlers
private Element getResourceElement(Application app, Element resElement,
GrammarInfo gInfo, Set<String> typeClassNames,
String type, File srcDir) {
if (type.length() > 0) {
if (type.startsWith("#")) {
String refId = type.substring(1);
List<Element> resourceTypes =
DOMUtils.getChildrenWithName(app.getAppElement(), WadlGenerator.WADL_NS,
"resource_type");
for (Element resourceType : resourceTypes) {
if (refId.equals(resourceType.getAttribute("id"))) {
Element realElement = (Element)resourceType.cloneNode(true);
DOMUtils.setAttribute(realElement, "id", resElement.getAttribute("id"));
DOMUtils.setAttribute(realElement, "path", resElement.getAttribute("path"));
return realElement;
}
}
} else {
URI wadlRef = URI.create(type);
String wadlRefPath = app.getWadlPath() != null
? getBaseWadlPath(app.getWadlPath()) + wadlRef.getPath() : wadlRef.getPath();
Application refApp = new Application(readIncludedDocument(wadlRefPath),
wadlRefPath);
GrammarInfo gInfoBase = generateSchemaCodeAndInfo(refApp, typeClassNames, srcDir);
if (gInfoBase != null) {
gInfo.getElementTypeMap().putAll(gInfoBase.getElementTypeMap());
gInfo.getNsMap().putAll(gInfo.getNsMap());
}
return getResourceElement(refApp, resElement, gInfo, typeClassNames,
"#" + wadlRef.getFragment(), srcDir);
}
}
return resElement;
}
private GrammarInfo getGrammarInfo(Element appElement, List<SchemaInfo> schemaElements) {
if (schemaElements == null || schemaElements.isEmpty()) {
return new GrammarInfo();
}
Map<String, String> nsMap = new HashMap<String, String>();
NamedNodeMap attrMap = appElement.getAttributes();
for (int i = 0; i < attrMap.getLength(); i++) {
Node node = attrMap.item(i);
String nodeName = node.getNodeName();
if (nodeName.startsWith("xmlns:")) {
String nsValue = node.getNodeValue();
nsMap.put(nodeName.substring(6), nsValue);
}
}
Map<String, String> elementTypeMap = new HashMap<String, String>();
for (SchemaInfo schemaEl : schemaElements) {
List<Element> elementEls = DOMUtils.getChildrenWithName(schemaEl.getElement(),
XmlSchemaConstants.XSD_NAMESPACE_URI, "element");
for (Element el : elementEls) {
String type = el.getAttribute("type");
if (type.length() > 0) {
String[] pair = type.split(":");
elementTypeMap.put(el.getAttribute("name"), pair.length == 1 ? pair[0] : pair[1]);
}
}
}
return new GrammarInfo(nsMap, elementTypeMap);
}
public void generateMainClass(Element resourcesEl, File src) {
}
private void writeResourceClass(Application app, Element rElement, Set<String> typeClassNames,
GrammarInfo gInfo, File src, boolean isRoot,
boolean interfaceIsGenerated) {
String resourceId = resourceName != null
? resourceName : rElement.getAttribute("id");
if (resourceId.length() == 0) {
resourceId = DEFAULT_RESOURCE_NAME;
}
boolean expandedQName = resourceId.startsWith("{") ? true : false;
QName qname = convertToQName(resourceId, expandedQName);
String namespaceURI = possiblyConvertNamespaceURI(qname.getNamespaceURI(), expandedQName);
if (getSchemaClassName(namespaceURI, gInfo, qname.getLocalPart(), typeClassNames) != null) {
return;
}
StringBuilder sbImports = new StringBuilder();
StringBuilder sbCode = new StringBuilder();
Set<String> imports = createImports();
final String classPackage = getClassPackageName(namespaceURI);
final String className = getClassName(qname.getLocalPart(), interfaceIsGenerated);
sbImports.append(getClassComment()).append(getLineSep());
sbImports.append("package " + classPackage)
.append(";").append(getLineSep()).append(getLineSep());
if (isRoot && writeAnnotations(interfaceIsGenerated)) {
String path = rElement.getAttribute("path");
writeAnnotation(sbCode, imports, Path.class, path, true, false);
}
sbCode.append("public " + getClassType(interfaceIsGenerated) + " " + className);
writeImplementsInterface(sbCode, qname.getLocalPart(), interfaceIsGenerated);
sbCode.append(" {" + getLineSep() + getLineSep());
writeMethods(rElement, imports, sbCode, typeClassNames, gInfo, isRoot, interfaceIsGenerated);
List<Element> childEls = DOMUtils.getChildrenWithName(rElement,
WadlGenerator.WADL_NS, "resource");
for (Element childEl : childEls) {
if (childEl.getAttribute("id").length() == 0) {
writeMethods(childEl, imports, sbCode, typeClassNames, gInfo, false, interfaceIsGenerated);
} else {
writeResourceMethod(childEl, imports, sbCode, typeClassNames, gInfo,
false, interfaceIsGenerated);
}
}
sbCode.append("}");
writeImports(sbImports, imports, classPackage);
createJavaSourceFile(src, new QName(classPackage, className), sbCode, sbImports);
for (Element subEl : childEls) {
String id = subEl.getAttribute("id");
if (id.length() > 0 && !resourceId.equals(id) && !id.startsWith("{java")
&& !id.startsWith("java")) {
writeResourceClass(app,
getResourceElement(app, subEl, gInfo, typeClassNames,
subEl.getAttribute("type"), src),
typeClassNames, gInfo, src, false, interfaceIsGenerated);
}
}
}
private QName convertToQName(String resourceId, boolean expandedQName) {
QName qname = null;
if (expandedQName) {
qname = JAXRSUtils.convertStringToQName(resourceId);
} else {
int lastIndex = resourceId.lastIndexOf(".");
qname = lastIndex == -1 ? new QName(resourceId)
: new QName(resourceId.substring(0, lastIndex),
resourceId.substring(lastIndex + 1));
}
return qname;
}
private String getClassType(boolean interfaceIsGenerated) {
return interfaceIsGenerated ? "interface" : "class";
}
private String getClassName(String clsName, boolean interfaceIsGenerated) {
if (interfaceIsGenerated) {
return clsName;
} else {
return generateInterfaces ? clsName + "Impl" : clsName;
}
}
private boolean writeAnnotations(boolean interfaceIsGenerated) {
if (interfaceIsGenerated) {
return true;
} else {
return !generateInterfaces && generateImpl;
}
}
private void writeImplementsInterface(StringBuilder sb, String clsName,
boolean interfaceIsGenerated) {
if (generateInterfaces && !interfaceIsGenerated) {
sb.append(" implements " + clsName);
}
}
private String getClassComment() {
return "/**"
+ getLineSep() + " * Created by Apache CXF WadlToJava code generator"
+ getLineSep() + "**/";
}
private void writeMethods(Element rElement,
Set<String> imports, StringBuilder sbCode,
Set<String> typeClassNames, GrammarInfo gInfo,
boolean isRoot,
boolean interfaceIsGenerated) {
List<Element> methodEls = DOMUtils.getChildrenWithName(rElement,
WadlGenerator.WADL_NS, "method");
for (Element methodEl : methodEls) {
writeResourceMethod(methodEl, imports, sbCode, typeClassNames, gInfo,
isRoot, interfaceIsGenerated);
}
}
private void writeAnnotation(StringBuilder sbCode, Set<String> imports,
Class<?> cls, String value, boolean nextLine, boolean addTab) {
if (value != null && value.length() == 0) {
return;
}
addImport(imports, cls.getName());
sbCode.append("@").append(cls.getSimpleName());
if (value != null) {
sbCode.append("(\"" + value + "\")");
}
if (nextLine) {
sbCode.append(getLineSep());
if (addTab) {
sbCode.append(TAB);
}
}
}
private void addImport(Set<String> imports, String clsName) {
if (imports == null || clsName.startsWith("java.lang")) {
return;
}
if (!imports.contains(clsName)) {
imports.add(clsName);
}
}
private void writeImports(StringBuilder sbImports, Set<String> imports, String classPackage) {
for (String clsName : imports) {
int index = clsName.lastIndexOf(".");
if (index != -1 && clsName.substring(0, index).equals(classPackage)) {
continue;
}
sbImports.append("import " + clsName).append(";").append(getLineSep());
}
}
private void writeResourceMethod(Element methodEl,
Set<String> imports, StringBuilder sbCode,
Set<String> typeClassNames, GrammarInfo gInfo,
boolean isRoot,
boolean interfaceIsGenerated) {
Element resourceEl = "resource".equals(methodEl.getLocalName())
? methodEl : (Element)methodEl.getParentNode();
String methodName = methodEl.getAttribute("name");
String methodNameLowerCase = methodName.toLowerCase();
String id = methodEl.getAttribute("id");
if (id.length() == 0) {
id = methodNameLowerCase;
}
List<Element> responseEls = DOMUtils.getChildrenWithName(methodEl,
WadlGenerator.WADL_NS, "response");
List<Element> requestEls = DOMUtils.getChildrenWithName(methodEl,
WadlGenerator.WADL_NS, "request");
if (writeAnnotations(interfaceIsGenerated)) {
sbCode.append(TAB);
if (methodNameLowerCase.length() > 0) {
if (HTTP_METHOD_ANNOTATIONS.containsKey(methodNameLowerCase)) {
writeAnnotation(sbCode, imports,
HTTP_METHOD_ANNOTATIONS.get(methodNameLowerCase), null, true, true);
} else {
// TODO : write a custom annotation class name based on HttpMethod
}
writeFormatAnnotations(requestEls, sbCode, imports, true);
writeFormatAnnotations(responseEls, sbCode, imports, false);
}
if (!isRoot) {
String path = resourceEl.getAttribute("path");
writeAnnotation(sbCode, imports, Path.class, path, true, true);
}
} else {
sbCode.append(getLineSep()).append(TAB);
}
if (!interfaceIsGenerated) {
sbCode.append("public ");
}
boolean responseTypeAvailable = true;
if (methodNameLowerCase.length() > 0) {
responseTypeAvailable = writeResponseType(responseEls, sbCode, imports, typeClassNames, gInfo);
sbCode.append(id);
} else {
boolean expandedQName = id.startsWith("{");
QName qname = convertToQName(id, expandedQName);
String packageName = possiblyConvertNamespaceURI(qname.getNamespaceURI(), expandedQName);
String clsSimpleName = getSchemaClassName(packageName, gInfo, qname.getLocalPart(),
typeClassNames);
String localName = clsSimpleName == null ? qname.getLocalPart()
: clsSimpleName.substring(packageName.length() + 1);
String subResponseNs = clsSimpleName == null ? getClassPackageName(packageName)
: clsSimpleName.substring(0, packageName.length());
String parentId = ((Element)resourceEl.getParentNode()).getAttribute("id");
writeSubResponseType(id.equals(parentId), subResponseNs, localName, sbCode, imports);
// TODO : we need to take care of multiple subresource locators with diff @Path
// returning the same type; also we might have ids like "{org.apache.cxf}Book#getName"
sbCode.append("get" + localName);
}
sbCode.append("(");
List<Element> inParamElements = new LinkedList<Element>();
inParamElements.addAll(DOMUtils.getChildrenWithName(resourceEl,
WadlGenerator.WADL_NS, "param"));
writeRequestTypes(requestEls, inParamElements, sbCode, imports, typeClassNames, gInfo,
interfaceIsGenerated);
sbCode.append(")");
if (interfaceIsGenerated) {
sbCode.append(";");
} else {
generateEmptyMethodBody(sbCode, responseTypeAvailable);
}
sbCode.append(getLineSep()).append(getLineSep());
}
private String possiblyConvertNamespaceURI(String nsURI, boolean expandedQName) {
return expandedQName ? getPackageFromNamespace(nsURI) : nsURI;
}
private String getPackageFromNamespace(String nsURI) {
return schemaPackageMap.containsKey(nsURI) ? schemaPackageMap.get(nsURI)
: PackageUtils.getPackageNameByNameSpaceURI(nsURI);
}
private void generateEmptyMethodBody(StringBuilder sbCode, boolean responseTypeAvailable) {
sbCode.append(" {");
sbCode.append(getLineSep()).append(TAB).append(TAB);
sbCode.append("//TODO: implement").append(getLineSep()).append(TAB);
if (responseTypeAvailable) {
sbCode.append(TAB).append("return null;").append(getLineSep()).append(TAB);
}
sbCode.append("}");
}
private boolean addFormParameters(List<Element> inParamElements, Element requestEl) {
List<Element> repElements =
DOMUtils.getChildrenWithName(requestEl, WadlGenerator.WADL_NS, "representation");
if (repElements.size() == 1) {
String mediaType = repElements.get(0).getAttribute("mediaType");
if (MediaType.APPLICATION_FORM_URLENCODED.equals(mediaType)) {
inParamElements.addAll(DOMUtils.getChildrenWithName(repElements.get(0),
WadlGenerator.WADL_NS, "param"));
return true;
}
}
return false;
}
private boolean writeResponseType(List<Element> responseEls, StringBuilder sbCode,
Set<String> imports, Set<String> typeClassNames,
GrammarInfo gInfo) {
List<Element> repElements = responseEls.size() == 1
? DOMUtils.getChildrenWithName(responseEls.get(0), WadlGenerator.WADL_NS, "representation")
: CastUtils.cast(Collections.emptyList(), Element.class);
if (repElements.size() == 0) {
sbCode.append("void ");
return false;
}
String elementName = getElementRefName(repElements, typeClassNames, gInfo, imports);
if (elementName != null) {
sbCode.append(elementName + " ");
} else {
addImport(imports, Response.class.getName());
sbCode.append("Response ");
}
return true;
}
private void writeSubResponseType(boolean recursive, String ns, String localName,
StringBuilder sbCode, Set<String> imports) {
if (!recursive && ns.length() > 0) {
addImport(imports, ns + "." + localName);
}
sbCode.append(localName).append(" ");
}
private void writeRequestTypes(List<Element> requestEls,
List<Element> inParamEls,
StringBuilder sbCode,
Set<String> imports,
Set<String> typeClassNames,
GrammarInfo gInfo,
boolean interfaceIsGenerated) {
boolean form = false;
boolean formParamsAvailbale = false;
if (requestEls.size() == 1 && inParamEls.size() == 0) {
inParamEls.addAll(DOMUtils.getChildrenWithName(requestEls.get(0),
WadlGenerator.WADL_NS, "param"));
int currentSize = inParamEls.size();
form = addFormParameters(inParamEls, requestEls.get(0));
formParamsAvailbale = currentSize < inParamEls.size();
}
if (form && !formParamsAvailbale) {
addImport(imports, MultivaluedMap.class.getName());
sbCode.append("MultivaluedMap map");
}
for (int i = 0; i < inParamEls.size(); i++) {
Element paramEl = inParamEls.get(i);
String name = paramEl.getAttribute("name");
if (writeAnnotations(interfaceIsGenerated)) {
Class<?> paramAnnotation = form ? FormParam.class
: PARAM_ANNOTATIONS.get(paramEl.getAttribute("style"));
writeAnnotation(sbCode, imports, paramAnnotation, name, false, false);
sbCode.append(" ");
}
String type = getPrimitiveType(paramEl);
if (Boolean.valueOf(paramEl.getAttribute("repeating"))) {
addImport(imports, List.class.getName());
type = "List<" + type + ">";
}
sbCode.append(type).append(" ").append(name.replace('.', '_'));
if (i + 1 < inParamEls.size()) {
sbCode.append(", ");
if (i + 1 >= 4 && ((i + 1) % 4) == 0) {
sbCode.append(getLineSep()).append(TAB).append(TAB).append(TAB).append(TAB);
}
}
}
if (!form) {
String elementName = null;
List<Element> repElements = requestEls.size() == 1
? DOMUtils.getChildrenWithName(requestEls.get(0), WadlGenerator.WADL_NS, "representation")
: CastUtils.cast(Collections.emptyList(), Element.class);
if (repElements.size() > 0) {
elementName = getElementRefName(repElements, typeClassNames, gInfo, imports);
}
if (elementName != null) {
if (inParamEls.size() > 0) {
sbCode.append(", ");
}
sbCode.append(elementName).append(" ").append(elementName.toLowerCase());
}
}
}
private String getPrimitiveType(Element paramEl) {
String type = paramEl.getAttribute("type");
if (type == null) {
return "String";
}
String[] pair = type.split(":");
String value = pair.length == 2 ? pair[1] : type;
return "string".equals(value) ? "String" : value;
}
private String getElementRefName(List<Element> repElements, Set<String> typeClassNames,
GrammarInfo gInfo, Set<String> imports) {
String elementRef = null;
for (Element el : repElements) {
String value = el.getAttribute("element");
if (value.length() > 0) {
elementRef = value;
break;
}
}
if (elementRef != null) {
String[] pair = elementRef.split(":");
if (pair.length == 2) {
String namespace = gInfo != null ? gInfo.getNsMap().get(pair[0]) : null;
if (namespace == null) {
return null;
}
String packageName = getPackageFromNamespace(namespace);
String clsName = getSchemaClassName(packageName, gInfo, pair[1], typeClassNames);
if (clsName != null) {
addImport(imports, clsName);
return clsName.substring(packageName.length() + 1);
}
}
}
return null;
}
private String getSchemaClassName(String packageName, GrammarInfo gInfo, String localName,
Set <String> typeClassNames) {
String clsName = matchClassName(typeClassNames, packageName, localName);
if (clsName == null && gInfo != null) {
clsName = matchClassName(typeClassNames, packageName,
gInfo.getElementTypeMap().get(localName));
}
return clsName;
}
private String matchClassName(Set<String> typeClassNames, String packageName, String localName) {
if (localName == null) {
return null;
}
String clsName = packageName + "." + localName;
for (String type : typeClassNames) {
if (type.toLowerCase().equals(clsName)) {
return type;
}
}
return null;
}
private void writeFormatAnnotations(List<Element> parentEls, StringBuilder sbCode,
Set<String> imports, boolean inRep) {
List<Element> repElements = parentEls.size() == 1
? DOMUtils.getChildrenWithName(parentEls.get(0), WadlGenerator.WADL_NS, "representation")
: CastUtils.cast(Collections.emptyList(), Element.class);
if (repElements.size() == 0) {
return;
}
Class<?> cls = inRep ? Consumes.class : Produces.class;
addImport(imports, cls.getName());
sbCode.append("@").append(cls.getSimpleName()).append("(");
if (repElements.size() > 1) {
sbCode.append("{");
}
for (int i = 0; i < repElements.size(); i++) {
String mediaType = repElements.get(i).getAttribute("mediaType");
if (mediaType != null) {
sbCode.append("\"" + mediaType + "\"");
if (i + 1 < repElements.size()) {
sbCode.append(", ");
}
}
}
if (repElements.size() > 1) {
sbCode.append(" }");
}
sbCode.append(")");
sbCode.append(getLineSep()).append(TAB);
}
private void createJavaSourceFile(File src, QName qname, StringBuilder sbCode, StringBuilder sbImports) {
String content = sbImports.toString() + getLineSep() + sbCode.toString();
String namespace = qname.getNamespaceURI();
generatedServiceClasses.add(namespace + "." + qname.getLocalPart());
namespace = namespace.replace(".", getFileSep());
File currentDir = new File(src.getAbsolutePath(), namespace);
currentDir.mkdirs();
File file = new File(currentDir.getAbsolutePath(), qname.getLocalPart() + ".java");
try {
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
fos.write(content.getBytes());
fos.close();
} catch (FileNotFoundException ex) {
LOG.warning(file.getAbsolutePath() + " is not found");
} catch (IOException ex) {
LOG.warning("Problem writing into " + file.getAbsolutePath());
}
}
private Application readWadl(String wadl, String docPath) {
return new Application(readXmlDocument(new StringReader(wadl)), docPath);
}
private Element readXmlDocument(Reader reader) {
try {
return StaxUtils.read(new InputSource(reader)).getDocumentElement();
} catch (Exception ex) {
throw new IllegalStateException("Unable to read wadl", ex);
}
}
private void generateClassesFromSchema(JCodeModel codeModel, File src) {
try {
Object writer = JAXBUtils.createFileCodeWriter(src);
codeModel.build(writer);
generatedTypeClasses = JAXBUtils.getGeneratedClassNames(codeModel);
} catch (Exception e) {
throw new IllegalStateException("Unable to write generated Java files for schemas: "
+ e.getMessage(), e);
}
}
private List<SchemaInfo> getSchemaElements(Application app) {
List<Element> grammarEls = DOMUtils.getChildrenWithName(app.getAppElement(),
WadlGenerator.WADL_NS, "grammars");
if (grammarEls.size() != 1) {
return null;
}
List<SchemaInfo> schemas = new ArrayList<SchemaInfo>();
List<Element> schemasEls = DOMUtils.getChildrenWithName(grammarEls.get(0),
XmlSchemaConstants.XSD_NAMESPACE_URI, "schema");
for (Element schemaEl : schemasEls) {
schemas.add(createSchemaInfo(schemaEl, app.getWadlPath()));
}
List<Element> includeEls = DOMUtils.getChildrenWithName(grammarEls.get(0),
WadlGenerator.WADL_NS, "include");
for (Element includeEl : includeEls) {
String href = includeEl.getAttribute("href");
String schemaURI = resolveLocationWithCatalog(href);
if (schemaURI == null) {
schemaURI = app.getWadlPath() != null ? getBaseWadlPath(app.getWadlPath()) + href : href;
}
schemas.add(createSchemaInfo(readIncludedDocument(schemaURI),
schemaURI));
}
return schemas;
}
private static String getBaseWadlPath(String docPath) {
int lastSep = docPath.lastIndexOf("/");
return lastSep != -1 ? docPath.substring(0, lastSep + 1) : docPath;
}
private SchemaInfo createSchemaInfo(Element schemaEl, String systemId) {
SchemaInfo info = new SchemaInfo(schemaEl.getAttribute("targetNamespace"));
info.setElement(schemaEl);
info.setSystemId(systemId);
return info;
}
private String resolveLocationWithCatalog(String href) {
if (bus != null) {
OASISCatalogManager catalogResolver = OASISCatalogManager.getCatalogManager(bus);
try {
return new OASISCatalogManagerHelper().resolve(catalogResolver,
href, null);
} catch (Exception e) {
throw new RuntimeException("Catalog resolution failed", e);
}
} else {
return null;
}
}
private Element readIncludedDocument(String href) {
try {
InputStream is = null;
if (!href.startsWith("http")) {
is = ResourceUtils.getResourceStream(href, bus);
}
if (is == null) {
is = URI.create(href).toURL().openStream();
}
return readXmlDocument(new InputStreamReader(is, "UTF-8"));
} catch (Exception ex) {
throw new RuntimeException("Resource " + href + " can not be read");
}
}
private JCodeModel createCodeModel(List<SchemaInfo> schemaElements, Set<String> type) {
SchemaCompiler compiler = createCompiler(type);
addSchemas(schemaElements, compiler);
for (InputSource is : bindingFiles) {
compiler.getOptions().addBindFile(is);
}
Object elForRun = ReflectionInvokationHandler
.createProxyWrapper(new InnerErrorListener(),
JAXBUtils.getParamClass(compiler, "setErrorListener"));
compiler.setErrorListener(elForRun);
S2JJAXBModel intermediateModel = compiler.bind();
JCodeModel codeModel = intermediateModel.generateCode(null, elForRun);
JAXBUtils.logGeneratedClassNames(LOG, codeModel);
return codeModel;
}
private SchemaCompiler createCompiler(Set<String> typeClassNames) {
return JAXBUtils.createSchemaCompilerWithDefaultAllocator(typeClassNames);
}
private void addSchemas(List<SchemaInfo> schemas, SchemaCompiler compiler) {
// handle package customizations first
for (int i = 0; i < schemaPackageFiles.size(); i++) {
compiler.parseSchema(schemaPackageFiles.get(i));
}
for (int i = 0; i < schemas.size(); i++) {
SchemaInfo schema = schemas.get(i);
String key = schema.getSystemId();
if (key != null) {
// TODO: CXF code should have a better solution somewhere, we'll get back to it
// when addressing the issue of retrieving WADLs with included schemas
if (key.startsWith("classpath:")) {
String resource = key.substring(10);
URL url = getClass().getResource(resource);
if (url != null) {
try {
key = url.toURI().toString();
} catch (Exception ex) {
// won't happen
}
}
}
} else {
key = Integer.toString(i);
}
InputSource is = new InputSource((InputStream)null);
is.setSystemId(key);
is.setPublicId(key);
compiler.getOptions().addGrammar(is);
compiler.parseSchema(key, schema.getElement());
}
}
public void setImportsComparator(Comparator<String> importsComparator) {
this.importsComparator = importsComparator;
}
private Set<String> createImports() {
return importsComparator == null ? new TreeSet<String>(new DefaultImportsComparator())
: new TreeSet<String>(importsComparator);
}
public void setGenerateInterfaces(boolean generateInterfaces) {
this.generateInterfaces = generateInterfaces;
}
public void setGenerateImplementation(boolean generate) {
this.generateImpl = generate;
}
public void setPackageName(String name) {
this.resourcePackageName = name;
}
public void setResourceName(String name) {
this.resourceName = name;
}
public void setWadlPath(String name) {
this.wadlPath = name;
}
public void setBindingFiles(List<InputSource> files) {
this.bindingFiles = files;
}
public void setSchemaPackageFiles(List<InputSource> files) {
this.schemaPackageFiles = files;
}
public void setSchemaPackageMap(Map<String, String> map) {
this.schemaPackageMap = map;
}
public void setBus(Bus bus) {
this.bus = bus;
}
public List<String> getGeneratedServiceClasses() {
return generatedServiceClasses;
}
public List<String> getGeneratedTypeClasses() {
return generatedTypeClasses;
}
private static class GrammarInfo {
private Map<String, String> nsMap = new HashMap<String, String>();
private Map<String, String> elementTypeMap = new HashMap<String, String>();
public GrammarInfo() {
}
public GrammarInfo(Map<String, String> nsMap, Map<String, String> elementTypeMap) {
this.nsMap = nsMap;
this.elementTypeMap = elementTypeMap;
}
public Map<String, String> getNsMap() {
return nsMap;
}
public Map<String, String> getElementTypeMap() {
return elementTypeMap;
}
}
private static class DefaultImportsComparator implements Comparator<String> {
private static final String JAVAX_PREFIX = "javax";
public int compare(String s1, String s2) {
boolean javax1 = s1.startsWith(JAVAX_PREFIX);
boolean javax2 = s2.startsWith(JAVAX_PREFIX);
if (javax1 && !javax2) {
return -1;
} else if (!javax1 && javax2) {
return 1;
} else {
return s1.compareTo(s2);
}
}
}
static class InnerErrorListener {
public void error(SAXParseException ex) {
throw new RuntimeException("Error compiling schema from WADL : "
+ ex.getMessage(), ex);
}
public void fatalError(SAXParseException ex) {
throw new RuntimeException("Fatal error compiling schema from WADL : "
+ ex.getMessage(), ex);
}
public void info(SAXParseException ex) {
// ignore
}
public void warning(SAXParseException ex) {
// ignore
}
}
private class Application {
private Element appElement;
private String wadlPath;
public Application(Element appElement, String wadlPath) {
this.appElement = appElement;
this.wadlPath = wadlPath;
}
public Element getAppElement() {
return appElement;
}
public String getWadlPath() {
return wadlPath;
}
}
}