Introduction
...
SchemaWizard
SchemaWizard (SW) maps an XML schema to HTML form elements and creates the framework and logic to guide users to generate instances of the schema through a Web interface. Users create new projects, configure SW for their needs, and publish resulting XML instances to any destionations, i.e. JMS servers, Web services, or through SMTP. User can also edit any previously created XML document using the same interface.
[Configuration and publication to be implemented.]
Schema Mapping
To generate XML instances based on XML schema elements, each schema element is mapped to one or more corresponding HTML form elements.
Simple, non-enumerating, non-repeating elements or attributes are mapped to a single input text field.
An enumerated simple type is mapped to a selection box with the enumerated values filled in.
An unbounded simple type is mapped to an input field, a selection box, and two buttons to add and remove new values independent from the form's main submission.
A defaulted simple type is mapped to an input field with a default value.
A fixed value simple type is mapped to no form element but the fixed value is written in plain text.
An unbounded complex value is mapped to, along with its subelements, a selection box where the element's index can be chosen, and a pair of add and remove buttons. If any complex element index is chosen from the list, that element's sub element values are filled in the form elements. [Not implemented yet!]
Example Mappings
XML Schema Element / HTML Mapping / XML Instance<xs:element name="anInteger"
type="xs:integer"/> / <input type="text" name="anInteger"
value=""> / <anInteger/
<xs:element name="enumString">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Value1"/>
<xs:enumeration value="Value2"/>
<xs:enumeration value="Value3"/>
</xs:restriction>
</xs:simpleType>
</xs:element> / <select name="enumString">
<option value="[remove]">
[remove]</option>
<option value="Value1">Value1
</option>
<option value="Value2">Value2
</option>
<option value="Value3">Value3
</option>
</select> / <enumString>Value1</enumString>
<xs:element name="unboundedInteger" type="xs:integer"
maxOccurs="unbounded"/> / <input type="text" name="unboundedInteger">
input type="submit" name="submit_unboundedInteger" value=" + ">
<select size="1" name="select_unboundedInteger">
<option value="-1">[select to remove]</option>
<option value="10">10</option>
<option value="99">99</option>
</select>
<input type="submit" name="remove_unboundedInteger" value=" - "> / <unboundedInteger>10
</unboundedInteger>
<unboundedInteger>99
</unboundedInteger>
<xs:element name="aFixedValue" type="xs:string"
fixed="This is fixed" /> / This is fixed. / <aFixedValue>This is fixed. </aFixedValue>
<xs:element name="aDefaultedValue" type="xs:string"
default="This is default value"/> / <input type="text" name="anInteger"
value="This is default value"> / <aDefaultedValue>This is default value.</aDefaultedValue>
<xs:element name="aBoolean" type="xs:boolean" /> / <input type="radio" name="aBoolean" value="true">Yes
<input type="radio" name="aBoolean" value="false">No / <aBoolean>true</aBoolean>
or
<aBoolean>false</aBoolean>
View Construction
SchemaWizard Architecture
The mapping process starts with parsing the XML schema. Castor API from Exolab.com provides Schema Object Modeling (SOM) which builds in-memory schema object from a schema source. SOM object has the necessary methods to traverse the schema element tree and obtain metadata about elements, attributes, content models, etc.
SW processes the schema object and generates a JSP file for each element which provides necessary HTML for the mapped form element and source code to set user input to a corresponding javabean as well as to obtain any data from the beans to fill in the form elements with previous or current values.
To generate JSP files, Velocity macro templates are used. One template is used for each type element, such as simple types, complex types, enumerated elements, unbounded elements, and index pages. SW obtains information about an element from the SOM object and based on its type, it invokes Velocity template engine with necessary variables set. The output is saved as a JSP nugget which will be included by the parent elements’ JSPs. For example, the following simplified template is used for enumerated types.
<% {// obtain element's name and its submitted value
String elementName = "$name";
String value = request.getParameter(elementName);
// obtain if this element has a parent. If root, no parent.
Stack parents = (Stack)request.getAttribute("_parents_stack");
String parent = (parents==null || parents.empty())
? null
: (String) parents.peek();
// check if form is submitted by either the main submit button,
// or other buttons on the form, such as those
// for unbounded elements
if(request.getAttribute("submit_parent") != null
& value != null)
{
// if [remove] item is selected, remove this element.
// otherwise set its value
if(value.equals(REMOVE))
${parentname}.set${javaname}(null);
else
try {
${parentname}.set${javaname}
(${javaname}Type.valueOf(value));
} catch(Exception ex) {}
}
else if(parent != null)
{
// if the form is not submitted by the main submit button,
// then bring back the in-memory value
Object valueObj = ${parentname}.get${javaname}();
value = (valueObj==null) ? null : valueObj.toString();
}
if(value == null)
value = "";
// enumerated values in an array
String[] values = {
#set ($i = 0)
#foreach ($enum in $values)
"$enum"#set ($i = $i + 1)#if( $i < $values.size() ) ,#end
#end
};
// send the HTML for enumeration
%>
<tr>
<td>
## if numbers is requested, then print out the leading numbers,
## i.e. 1. , 2.2, 3.4.2
#if ($numbers)
<%= ((Leveler)request.getAttribute("_leveler")).increase()%>
#end
$label</td>
<td>
<select name="$name">
<option value="<%=REMOVE%>"<%=REMOVE%</option<%
// print out option. select the one that matches in-memory value
for(int i=0; i< values.length; i++)
{
String enm = values[i]; %>
<option value="<%= enm %>"<%=
(value != null & value.equals(enm))
? " SELECTED"
:"" %<%= enm %>
</option<%
} %>
</select>
</td>
</tr<%
} %>
After this template is processed for a schema enumerated element, given as an example in the mappings table, enumString, the following JSP nugget is obtained.
<% {// obtain element's name and its submitted value
String elementName = "enumString";
String value = request.getParameter(elementName);
// obtain if this element has a parent. If root, no parent.
Stack parents = (Stack)request.getAttribute("_parents_stack");
String parent = (parents==null || parents.empty())
? null
: (String) parents.peek();
// check if form is submitted by either the main submit button,
// or other buttons on the form, such as those
// for unbounded elements
if(request.getAttribute("submit_parent") != null
& value != null)
{
// if [remove] item is selected, remove this element.
// otherwise set its value
if(value.equals(REMOVE))
rootElement.setEnumString(null);
else
try {
rootElement.setEnumString(EnumStringType.valueOf(value));
} catch(Exception ex) {}
}
else if(parent != null)
{
// if the form is not submitted by the main submit button,
// then bring back the in-memory value
Object valueObj = rootElement.getEnumString();
value = (valueObj==null) ? null : valueObj.toString();
}
if(value == null)
value = "";
// enumerated values in an array
String[] values = {
"Value1" ,"Value2" ,"Value3" };
// send the HTML for enumeration
%>
<tr>
<td>
<%= ((Leveler)request.getAttribute("_leveler")).increase()%>
Enumstring:</td>
<td>
<select name="enumString">
<option value="<%=REMOVE%>"<%=REMOVE%</option<%
// print out option. select the one that matches in-memory value
for(int i=0; i< values.length; i++)
{
String enm = values[i]; %>
<option value="<%= enm %>"<%=
(value != null & value.equals(enm))
? " SELECTED"
:"" %<%= enm %>
</option<%
} %>
</select>
</td>
</tr<%
} %>
All variable defined in aenerated JSP nuggets are local. Since parent elements include sub elements’ JSPs, initializations are performed within parent’s scope. The following is an example generated JSP for a parent element.
<jsp:useBean id="rootElement" scope="session"class="testproject.RootElement" />
<%
{
boolean submit_rootElement =
(request.getParameter("submit_rootElement")!=null);
Stack parents = ((Stack)request.getAttribute("_parents_stack"));
if(parents.empty()) {
// if this is the root element, then all the sub elements
// must be inserted at the click of the submit button.
if(submit_rootElement)
request.setAttribute("submit_parent", "true");
else
if(request.getAttribute("submit_parent")!=null)
request.removeAttribute("submit_parent");
}
parents.push("rootElement");
}
%>
<tr>
<td<b>
<%= ((Leveler)request.getAttribute("_leveler")).increase()%>
Rootelement:</b</td</tr>
<% ((Leveler)request.getAttribute("_leveler")).newLevel(); %>
<%-- include elements here --%>
<%@ include file="aString/aString.jsp" %>
<%@ include file="unboundedString/unboundedString.jsp" %>
<%@ include file="enumString/enumString.jsp" %>
<%@ include file="anInteger/anInteger.jsp" %>
<%@ include file="unboundedInteger/unboundedInteger.jsp" %>
<%@ include file="enumInteger/enumInteger.jsp" %>
<%@ include file="aComplexElement/aComplexElement.jsp" %>
<%@ include file="aFixedValue/aFixedValue.jsp" %>
<%@ include file="aDefaultedValue/aDefaultedValue.jsp" %>
<%@ include file="aBoolean/aBoolean.jsp" %>
<%@ include file="aReferencedElement/aReferencedElement.jsp" %>
<%{
Stack parents = ((Stack)request.getAttribute("_parents_stack"));
String elementName = (String)parents.pop();
((Leveler)request.getAttribute("_leveler")).killLevel();
if(parents.empty()) {
// if this is the root element, then put a submit button.
%>
<tr>
<td<input type="submit" name="submit_rootElement"
value="Submit"</td>
</tr<%
}
}
%>
In XML schema, all the elements defined as global elements can be referenced at any level within the schema hierarchy. To support referenced global elements, we have designed the parent elements’ JSPs, such as the above one, so that theycan be included by other complex typed elements. The above element ‘rootElement’, can repeat in any part of the schema as well as its mapped HTML form within the HTML form hierarchy.
The entry pages for HTML forms are index pages which must not be included by other JSPs. SW does not provide such inclusions. Users of generated JSPs should also notice this requirement. The following is an index pages for the element rootElement which includes further declarations and initialization for the form.
<%! final static String REMOVE = "[remove]"; %><%-- numbers are printed. --%>
<%@ page import="commgrids.metadata.leveler.Leveler" %>
<jsp:useBean id="_leveler" scope="request"
class="commgrids.metadata.leveler.Leveler" />
<%
_leveler.setLeveler(true);
String errorMsg = null;
%>
<%-- Project’s Java package generated
by Castor SourceGenerator included here --%>
<%@ page import="testproject.*,testproject.types.*" %>
%@ page import="java.util.*" %>
<jsp:useBean id="_parents_stack"
scope="request" class="java.util.Stack" />
<%
request.setAttribute("_parents_stack",_parents_stack);
%>
<html<body>
<form method="post">
<table width="100%">
<tr>
<td valign="top">
<%-- sub elements’ JSPs are included here --%>
<table>
<%@ include file="rootElement.jsp" %>
</table>
</td>
<%-- marshal the java bean XML into an text area --%>
<td valign="top" align="right" >
<textarea rows="50" cols="60"
disabled<%
try {
rootElement.marshal(out);
} catch(Exception ex) {
errorMsg = ex.getMessage();
}
%</textarea>
</td>
</tr>
</table>
</form>
<%
if(errorMsg != null)
{
%<p<font color="red"<%=errorMsg%</font</p<%
}
%>
</body>
</html>
Source Generation
Apart from the source code within JSP pages to set and get values for form elements, SW generates Javabeans to keep values for complex elements by using Castor’s SourceGenerator (SG). SG takes the XML schema source as its input and generates Java classes, or javabeans, corresponding to schema elements. These javabeans are equipped with XML marshalling and unmarshalling methods which we use to generate the final XML instance.
Upon generating the javabeans, SW compiles them and readies for JSP imports and usage. The final project is deployed as a separate Web application whose JSP pages, compiled classes and libraries are already installed.