package org.saintandreas.serket.scpd.util;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.xpath.XPathExpressionException;
import org.saintandreas.serket.impl.BaseService;
import org.saintandreas.serket.soap.SOAPSerializable;
import org.saintandreas.util.XPathUtil;
import org.saintandreas.util.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.sun.codemodel.ClassType;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JForEach;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.codemodel.writer.FileCodeWriter;
public class GenerateScpdClasses {
private final static String URI_PREFIX = "urn:schemas-upnp-org:service:";
public static void main(String[] args) throws JClassAlreadyExistsException, IOException, XPathExpressionException, SAXException, ParserConfigurationException {
JCodeModel cm = new JCodeModel();
JPackage pkg = cm._package("org.saintandreas.serket.scpd");
File scpdDir = new File("src/main/resources/scpd");
File[] dirs = scpdDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".xml");
}
});
for (File f : dirs) {
String serviceName = f.getName();//"ContentDirectory3";
serviceName = serviceName.substring(0, serviceName.length() - 4);
Document scpdDoc = XmlUtil.parseXmlFile(f);
JDefinedClass cls = pkg._class(JMod.PUBLIC | JMod.ABSTRACT, serviceName, ClassType.CLASS);
{
JClass type = (JClass) cm._ref(BaseService.class);
cls._extends(type);
JMethod method = cls.constructor(JMod.PUBLIC);
method.param(String.class, "id");
method.param(String.class, "controlURL");
method.param(String.class, "eventURL");
method.body().directStatement("super(id, controlURL, eventURL);") ;
}
// public static final String URI = "urn:schemas-upnp-org:service:ContentDirectory:1";
{
String uriServiceName = serviceName;
String uriVersionNumber = "1";
if (Character.isDigit(serviceName.charAt(serviceName.length() - 1))) {
uriVersionNumber = serviceName.substring(serviceName.length() - 1);
uriServiceName = serviceName.substring(0, serviceName.length() - 1);
}
JVar uriDecl = cls.field(JMod.STATIC | JMod.PUBLIC | JMod.FINAL, String.class, "URI").init(JExpr.lit(URI_PREFIX + uriServiceName + ":" + uriVersionNumber));
cls.method(JMod.PUBLIC, String.class, "getURI").body()._return(uriDecl);
}
for (Element e : XPathUtil.getElements(scpdDoc, "/scpd/actionList/action")) {
createAction(cm, cls, e);
}
// for (Element e : XPathUtil.getElements(scpdDoc, "/scpd/serviceStateTable/stateVariable")) {
// createField(cls, e);
// }
}
File dest = new File("src/main/java/");
dest.mkdirs();
cm.build(new FileCodeWriter(dest));
}
private static void createAction(JCodeModel cm, JDefinedClass cls, Element e) throws XPathExpressionException, JClassAlreadyExistsException {
String name = XPathUtil.getStringValue(e, "name").trim();
String methodName = name.substring(0, 1).toLowerCase() + name.substring(1);
Element[] outArgs = XPathUtil.getElements(e, "argumentList/argument[direction='out']");
Element[] inArgs = XPathUtil.getElements(e, "argumentList/argument[direction='in']");
JType returnType = getArgumentsType(cm, cls, name, outArgs, true);
JType inputType = getArgumentsType(cm, cls, name, inArgs, false);
JMethod method = cls.method(JMod.PUBLIC | JMod.ABSTRACT, returnType, methodName);
method._throws(IOException.class);
method._throws(ServletException.class);
if (inputType != cm.VOID) {
method.param(inputType, "input");
}
}
private static JType getArgumentsType(JCodeModel cm, JDefinedClass cls, String name, Element[] args, boolean out) throws XPathExpressionException, JClassAlreadyExistsException {
JDefinedClass returnClass = cls._class(JMod.PUBLIC | JMod.STATIC , name + (out ? "Response" : "Request"));
returnClass._extends(SOAPSerializable.class);
// public void parse(SOAPMessage soapmessage) throws SOAPException;
JMethod parse = returnClass.method(JMod.PUBLIC, cm.VOID, "parse");
JVar soapMessageParam = parse.param(SOAPMessage.class, "soapMessage");
// public SOAPMessage format()
JMethod format = returnClass.method(JMod.PUBLIC, SOAPMessage.class, "format");
format._throws(SOAPException.class);
JVar formatRetVal = format.body().decl(cm.ref(SOAPMessage.class), "retVal").init(returnClass.staticInvoke("createMessage"));
JInvocation formatBodyElementInvocation = formatRetVal.invoke("getSOAPBody").invoke("addBodyElement").arg(JExpr._new(cm.ref(QName.class)).arg(JExpr.ref("URI")).arg(JExpr.lit(returnClass.name())).arg(JExpr.lit("u")));
JClass xmlUtilClass = cm.ref(XmlUtil.class);
if (args.length > 0) {
parse.annotate(SuppressWarnings.class).param("value", "unchecked");
parse._throws(SOAPException.class);
// for (Element e : XmlUtil.getChildElements(XmlUtil.getChildElements(body).get(0))) {
// for (Element e : XmlUtil.getChildElements(XmlUtil.getChildElements(soapMessage.getSOAPBody()).get(0))) {
JInvocation collection = xmlUtilClass.staticInvoke("getChildElements").arg(xmlUtilClass.staticInvoke("getChildElements").arg(soapMessageParam.invoke("getSOAPBody")).invoke("get").arg(JExpr.lit(0)));
JForEach parseFor = parse.body().forEach(cm._ref(Element.class), "e", collection);
JVar parseForElement = parseFor.var();
JVar parseForName = parseFor.body().decl(cm.ref(String.class), "name", parseForElement.invoke("getNodeName"));
JVar formatElement = format.body().decl(cm.ref(SOAPBodyElement.class), "soapBodyElement",formatBodyElementInvocation);
for (Element outArg : args) {
String relatedStateVariable = XPathUtil.getStringValue(outArg, "relatedStateVariable").trim();
String elementName = XPathUtil.getStringValue(outArg, "name").trim();
String fieldName = Character.toLowerCase(elementName.charAt(0)) + elementName.substring(1);
JType fieldType = getStateVariableType(cm, cls, relatedStateVariable, outArg.getOwnerDocument());
JFieldVar fieldVar = returnClass.field(JMod.PUBLIC, fieldType, fieldName);
// element.addChildElement("Result").setTextContent(resultDidl);
JInvocation formatSetText = formatElement.invoke("addChildElement").arg(JExpr.lit(elementName)).invoke("setTextContent");
format.body().add(formatSetText);
// if ("Result".equals(name)) {
JBlock ifBody = parseFor.body()._if(JExpr.lit(elementName).invoke("equals").arg(parseForName))._then();
// String value = e.getTextContent();
if (fieldType.isPrimitive()) {
formatSetText.arg(cm.ref(Integer.class).staticInvoke("toString").arg(fieldVar));
} else {
formatSetText.arg(fieldVar.invoke("toString"));
}
if (fieldType.equals(cm.ref(String.class))) {
// TransferID = e.getTextContent();
ifBody.assign(fieldVar, parseForElement.invoke("getTextContent"));
} else if (fieldType.equals(cm.ref(Integer.class)) || fieldType.equals(cm.INT)) {
// TransferID = Integer.valueOf(e.getTextContent());
ifBody.assign(fieldVar, cm.ref(Integer.class).staticInvoke("valueOf").arg(parseForElement.invoke("getTextContent")));
} else if (fieldType.equals(cm.ref(Boolean.class)) || fieldType.equals(cm.BOOLEAN)) {
// TransferID = Integer.valueOf(e.getTextContent());
ifBody.assign(fieldVar, cm.ref(Boolean.class).staticInvoke("valueOf").arg(parseForElement.invoke("getTextContent")));
} else if (fieldType instanceof JClass) {
JClass fieldCls = (JClass) fieldType;
// TransferStatus = org.saintandreas.serket.scpd.TransferStatus.valueOf(e.getTextContent());
ifBody.assign(fieldVar, fieldCls.staticInvoke("valueOf").arg(parseForElement.invoke("getTextContent")));
} else {
System.out.println("unparsed type " + fieldType);
}
// JVar value = ifBody.decl(cm.ref(String.class), "value", );
ifBody._continue();
}
} else {
format.body().add(formatBodyElementInvocation);
}
format.body()._return(formatRetVal);
return returnClass;
}
private static JType getStateVariableType(JCodeModel cm, JDefinedClass ownerClass, String stateVariable, Document document) throws XPathExpressionException {
Node originalVariable = XPathUtil.getNode(document, "/scpd/serviceStateTable/stateVariable[contains(name, '" + stateVariable + "')]");
if (originalVariable == null) {
String xpath = "/scpd/serviceStateTable/stateVariable[contains(name, '" + stateVariable + "')]";
originalVariable = XPathUtil.getNode(document, xpath);
}
return getStateVariableType(cm, ownerClass, originalVariable);
}
private static JType getStateVariableType(JCodeModel cm, JDefinedClass ownerClass, Node stateVariable) throws XPathExpressionException {
JType retVal = null;
String dataType = XPathUtil.getStringValue(stateVariable, "dataType").trim();
boolean isEnum = XPathUtil.getNode(stateVariable, "allowedValueList") != null;
boolean optional = null != XPathUtil.getNode(stateVariable, "Optional");
if (isEnum) {
JPackage pkg = cm._package("org.saintandreas.serket.scpd");
String name = XPathUtil.getStringValue(stateVariable, "name").trim();
if (name.startsWith("A_ARG_TYPE_")) {
name = name.substring("A_ARG_TYPE_".length());
}
try {
JDefinedClass enumCls = ownerClass._enum(name);
for (String s : XPathUtil.getStrings(stateVariable, "allowedValueList/allowedValue")) {
enumCls.enumConstant(s.trim());
}
retVal = enumCls;
} catch (JClassAlreadyExistsException e) {
retVal = cm.ref(pkg.name() + "." + ownerClass.name() + "." + name);
}
} else if ("string".equals(dataType)) {
retVal = cm._ref(String.class);
} else if ("boolean".equals(dataType)) {
retVal = cm._ref(optional ? Boolean.class : boolean.class);
} else if ("uri".equals(dataType)) {
retVal = cm._ref(String.class);
} else if (dataType.startsWith("ui") || dataType.startsWith("i")) {
retVal = cm._ref(optional ? Integer.class : int.class);
}
if (retVal == null) {
throw new RuntimeException("unknown type");
}
return retVal;
}
// private static void createField(JDefinedClass cls, Element e) throws XPathExpressionException {
// String name = XPathUtil.getStringValue(e, "name").trim();
// boolean eventsAttr = "yes".equalsIgnoreCase(XPathUtil.getStringValue(e, "sendEventsAttribute").trim());
// }
}