/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.wso2.carbon.dataservices.core;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.axis2.AxisFault;
import org.apache.axis2.description.AxisMessage;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.WSDL11ToAxisServiceBuilder;
import org.apache.axis2.description.WSDL20ToAxisServiceBuilder;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.schema.CompilerOptions;
import org.apache.axis2.schema.SchemaCompilationException;
import org.apache.axis2.schema.SchemaCompiler;
import org.apache.axis2.util.XMLPrettyPrinter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.apache.ws.commons.schema.XmlSchemaParticle;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaType;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wso2.carbon.dataservices.common.DBConstants;
import org.wso2.carbon.dataservices.core.description.config.Config;
import org.wso2.carbon.dataservices.core.description.config.RDBMSConfig;
import org.wso2.carbon.dataservices.core.description.operation.Operation;
import org.wso2.carbon.dataservices.core.description.query.SQLQuery;
import org.wso2.carbon.dataservices.core.engine.CallQuery;
import org.wso2.carbon.dataservices.core.engine.CallQuery.WithParam;
import org.wso2.carbon.dataservices.core.engine.CallQueryGroup;
import org.wso2.carbon.dataservices.core.engine.DataService;
import org.wso2.carbon.dataservices.core.engine.DataServiceSerializer;
import org.wso2.carbon.dataservices.core.engine.OutputElementGroup;
import org.wso2.carbon.dataservices.core.engine.ParamValue;
import org.wso2.carbon.dataservices.core.engine.QueryParam;
import org.wso2.carbon.dataservices.core.engine.Result;
import org.wso2.carbon.dataservices.core.engine.StaticOutputElement;
import org.wso2.carbon.dataservices.core.validation.Validator;
/**
* This class is used to create a data service using a given WSDL (create
* contract first data services).
*/
public class WSDLToDataService {
private static final Log log = LogFactory.getLog(WSDLToDataService.class);
private static final String WSDL20_ROOT_ELEMENT = "description";
private WSDLToDataService() {
}
/**
* Checks if the given data is from a WSDL2.0 document.
* @param wsdlContent The WSDL document data
* @return true if it is a WSDL2.0 document
* @throws DataServiceFault
*/
private static boolean isWSDL20(byte[] wsdlContent) throws DataServiceFault {
try {
return (AXIOMUtil.stringToOM(
new String(wsdlContent, DBConstants.DEFAULT_CHAR_SET_TYPE))
.getLocalName().equals(WSDL20_ROOT_ELEMENT));
} catch (Exception e) {
throw new DataServiceFault(e);
}
}
/**
* Creates and deploys a contract first data service with the given WSDL
* data.
* @param axisConfig The current axis configuration
* @param wsdlContent The WSDL content
* @throws DataServiceFault
*/
public static void deployDataService(AxisConfiguration axisConfig,
byte[] wsdlContent) throws DataServiceFault {
try {
AxisService axisService = getAxisServiceFromWSDL(wsdlContent);
String serviceName = axisService.getName();
DataService dataService = createDataServiceFromAxisService(axisService);
String dsContents = DataServiceSerializer.serializeDataService(dataService).toString();
writeToRepository(axisConfig, serviceName, dsContents);
} catch (DataServiceFault e) {
log.error("Error in deploying contract first data service", e);
throw e;
}
}
/**
* Populates and returns an AxisService from a WSDL.
* @param wsdlContent The WSDL content
* @return AxisService which represents the given WSDL
* @throws DataServiceFault
*/
private static AxisService getAxisServiceFromWSDL(byte[] wsdlContent) throws DataServiceFault {
try {
AxisService axisService;
ByteArrayInputStream byteIn = new ByteArrayInputStream(wsdlContent);
if (isWSDL20(wsdlContent)) {
axisService = new WSDL20ToAxisServiceBuilder(byteIn, null, null).populateService();
} else { // Must be WSDL11
axisService = new WSDL11ToAxisServiceBuilder(byteIn, null, null).populateService();
}
return axisService;
} catch (AxisFault e) {
String message = "Error in getting AxisService from WSDL";
throw new DataServiceFault(e, message);
}
}
/**
* Write the data service to the deployment directory.
* @param axisConfig The current AxisConfiguration
* @param serviceName The name of the service to be deployed
* @param dsContents The contents of the data service configuration
* @throws DataServiceFault
*/
private static void writeToRepository(AxisConfiguration axisConfig,
String serviceName, String dsContents) throws DataServiceFault {
try {
URL repositoryURL = axisConfig.getRepository();
String dataservicesFile = repositoryURL.getPath() + File.separator
+ DBDeployer.DEPLOYMENT_FOLDER_NAME + File.separator
+ serviceName + "." + DBConstants.DBS_FILE_EXTENSION;
File parentFile = new File(dataservicesFile).getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
BufferedWriter writer = new BufferedWriter(new FileWriter(dataservicesFile));
writer.write(dsContents);
writer.close();
XMLPrettyPrinter.prettify(new File(dataservicesFile));
} catch (IOException e) {
String message = "Error in writing the contract first data service to the repository";
throw new DataServiceFault(e, message);
}
}
/**
* Creates a dummy data source config.
* @param dataService The current data service
* @param configId The configuration id of the config to be created
* @return The newly created dummy config
* @throws DataServiceFault
*/
private static Config getDummyConfig(DataService dataService,
String configId) throws DataServiceFault {
Map<String, String> props = new HashMap<String, String>();
props.put(DBConstants.RDBMS.DRIVER, null);
props.put(DBConstants.RDBMS.PROTOCOL, null);
props.put(DBConstants.RDBMS.USER, null);
props.put(DBConstants.RDBMS.PASSWORD, null);
props.put(DBConstants.RDBMS.MIN_POOL_SIZE, null);
props.put(DBConstants.RDBMS.MAX_POOL_SIZE, null);
props.put(DBConstants.RDBMS.VALIDATION_QUERY, null);
RDBMSConfig config = new RDBMSConfig(dataService, configId, props);
return config;
}
private static String extractServiceNameFromHeirachicalName(String name) {
int lastIndex = name.length() - 1;
int index = name.lastIndexOf('\\');
if (index != -1 && index < lastIndex) {
return name.substring(index + 1);
}
index = name.lastIndexOf('/');
if (index != -1 && index < lastIndex) {
return name.substring(index + 1);
}
return name;
}
/**
* Create a DataService from an AxisService.
* @param axisService The AxisService used to create the DS
* @return The newly created data service
* @throws DataServiceFault
*/
@SuppressWarnings("unchecked")
private static DataService createDataServiceFromAxisService(
AxisService axisService) throws DataServiceFault {
DataService dataService = new DataService(
extractServiceNameFromHeirachicalName(axisService.getName()),
null, null, null, DBConstants.ServiceStatusValues.INACTIVE,
false, false, false, null, false);
/* add dummy config */
String dummyConfigId = DBConstants.DEFAULT_CONFIG_ID;
dataService.addConfig(getDummyConfig(dataService, dummyConfigId));
/* compile schema */
Map<QName, Document> modelMap;
Map<QName, String> elementMap;
try {
CompilerOptions options = new CompilerOptions();
SchemaCompiler schemaCompiler = new SchemaCompiler(options);
schemaCompiler.compile(axisService.getSchema());
modelMap = schemaCompiler.getProcessedModelMap();
elementMap = schemaCompiler.getProcessedElementMap();
} catch (SchemaCompilationException e) {
throw new DataServiceFault(e, "Error in schema compile");
}
/* add queries/operations */
AxisOperation axisOperation;
String operationName;
String queryId;
List<QueryParam> queryParams;
for (Iterator<AxisOperation> axisOperations = axisService.getOperations(); axisOperations.hasNext();) {
axisOperation = axisOperations.next();
operationName = axisOperation.getName().getLocalPart();
queryId = operationName + DBConstants.CONTRACT_FIRST_QUERY_SUFFIX;
queryParams = getQueryParamsFromAxisOperation(modelMap, elementMap, axisOperation);
/* query */
dataService.addQuery(new SQLQuery(dataService, queryId, dummyConfigId, false, null,
DBConstants.CONTRACT_FIRST_DUMMY_SQL, queryParams,
getResultFromAxisOperation(dataService, axisOperation), null, null,
new HashMap<String, String>(),
dataService.getServiceNamespace()));
/* operation */
dataService.addOperation(new Operation(dataService, operationName, null,
getOperationCallQueryGroupFromQueryParams(dataService, queryId, queryParams),
false, null, false, false));
}
return dataService;
}
private static CallQueryGroup getOperationCallQueryGroupFromQueryParams(DataService dataService, String queryId,
List<QueryParam> queryParams) throws DataServiceFault {
Map<String, WithParam> withParams = new HashMap<String, WithParam>();
for (QueryParam qp : queryParams) {
withParams.put(qp.getName(),
new WithParam(qp.getName(), qp.getName(),
qp.getName(), DBConstants.DBSFields.QUERY_PARAM));
}
CallQuery callQuery = new CallQuery(dataService, queryId, withParams, new HashSet<String>());
List<CallQuery> callQueries = new ArrayList<CallQuery>();
callQueries.add(callQuery);
CallQueryGroup callQueryGroup = new CallQueryGroup(callQueries);
return callQueryGroup;
}
private static AxisMessage getAxisMessageFromOperation(
AxisOperation axisOperation, String direction) {
Iterator<AxisMessage> msgs = axisOperation.getMessages();
AxisMessage tmpAxisMessage = null;
while (msgs.hasNext()) {
tmpAxisMessage = msgs.next();
if (tmpAxisMessage.getDirection().equals(direction)) {
return tmpAxisMessage;
}
}
return null;
}
/**
* Prints the given DOM document,
* used for debugging purposes.
*/
public static void printDOM(Document dom) {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
DOMSource domSource = new DOMSource(dom);
StreamResult streamResult = new StreamResult(System.out);
transformer.transform(domSource, streamResult);
} catch (Exception e) {
e.printStackTrace();
}
}
private static List<QueryParam> getQueryParamsFromAxisOperation(Map<QName, Document> modelMap,
Map<QName, String> elementMap, AxisOperation axisOperation) throws DataServiceFault {
AxisMessage axisMessage = getAxisMessageFromOperation(axisOperation, "in");
if (axisMessage == null) {
throw new DataServiceFault(
"Valid in message cannot be found for the operation '"
+ axisOperation.getName() + "'");
}
XmlSchemaElement inMsgElement = axisMessage.getSchemaElement();
/* no query params - return empty list */
if (inMsgElement == null) {
return new ArrayList<QueryParam>();
}
XmlSchemaType inMsgType = inMsgElement.getSchemaType();
if (!(inMsgType instanceof XmlSchemaComplexType)) {
throw new DataServiceFault(
"Xmlschema complex type is expected for the in message of the operation '"
+ axisOperation.getName() + "'");
}
QName inMsgTypeName = inMsgElement.getQName();
String elementName = elementMap.get(inMsgTypeName);
Document operationDoc = modelMap.get(new QName(inMsgTypeName.getNamespaceURI(), elementName));
ModelBean operationBean = createModelBean(modelMap, operationDoc);
List<QueryParam> queryParams = new ArrayList<QueryParam>();
String tmpType;
List<ModelProperty> props = operationBean.getProperties();
ModelProperty prop;
int count = props.size();
for (int i = 0; i < count; i++) {
prop = props.get(i);
tmpType = prop.getSimpleType();
if (tmpType == null) {
if (prop.getType().isSimple()) {
tmpType = prop.getType().getProperties().get(0).getSimpleType();
} else {
throw new DataServiceFault(
"A list of elements with simple types are expected at the in message of the operation '"
+ axisOperation.getName() + "'");
}
}
queryParams.add(new QueryParam(
prop.getName(),
DBUtils.getSQLTypeFromXsdType(tmpType),
DBConstants.QueryTypes.IN,
prop.isArray() ? DBConstants.QueryParamTypes.ARRAY :
DBConstants.QueryParamTypes.SCALAR,
i + 1, // ordinal
null, new ArrayList<Validator>()));
}
return queryParams;
}
private static Result getResultFromAxisOperation(DataService dataService, AxisOperation axisOperation)
throws DataServiceFault {
AxisMessage axisMessage = getAxisMessageFromOperation(axisOperation, "out");
// if no out message, then no result
if (axisMessage == null) {
return null;
}
String elementName = null, rowName = null, namespace = null;
XmlSchemaElement wrapperSchemaElement = axisMessage.getSchemaElement();
elementName = wrapperSchemaElement.getName();
namespace = wrapperSchemaElement.getQName().getNamespaceURI();
XmlSchemaType wrapperSchemaType = wrapperSchemaElement.getSchemaType();
if (!(wrapperSchemaType instanceof XmlSchemaComplexType)) {
throw new DataServiceFault(
"Xmlschema complex type is expected for the out message of the operation '"
+ axisOperation.getName() + "'");
}
XmlSchemaComplexType wrapperSchemaComplexType = (XmlSchemaComplexType) wrapperSchemaType;
XmlSchemaComplexType dataFieldsType;
if (hasResultRowName(wrapperSchemaComplexType)) {
rowName = getResultRowName(wrapperSchemaComplexType);
dataFieldsType = getRowNameBasedSchemaComplexType(wrapperSchemaComplexType);
} else {
dataFieldsType = wrapperSchemaComplexType;
}
Result result = new Result(elementName, rowName, namespace, null, DBConstants.ResultTypes.XML);
OutputElementGroup defGroup = new OutputElementGroup(null, null, null);
XmlSchemaObjectCollection dataSchemaObjects = getSchemaObjectsFromComplexType(dataFieldsType);
int count = dataSchemaObjects.getCount();
XmlSchemaObject sequenceDataObject;
XmlSchemaElement sequenceDataElement;
XmlSchemaAttribute sequenceDataAttr;
for (int i = 0; i < count; i++) {
sequenceDataObject = dataSchemaObjects.getItem(i);
if (sequenceDataObject instanceof XmlSchemaElement) {
sequenceDataElement = (XmlSchemaElement) sequenceDataObject;
if (!(sequenceDataElement.getSchemaType() instanceof XmlSchemaSimpleType)) {
throw new DataServiceFault(
"Xmlschema sequence's data fields at the out message of the operation '"
+ axisOperation.getName() +
"' should only contain xml elements with simple types");
}
defGroup.addElementEntry(new StaticOutputElement(dataService,
sequenceDataElement.getName(), sequenceDataElement
.getName(), sequenceDataElement.getName(),
DBConstants.DBSFields.COLUMN,
DBConstants.DBSFields.ELEMENT, namespace,
sequenceDataElement.getSchemaTypeName(),
new HashSet<String>(), DBConstants.DataCategory.VALUE,
DBConstants.ResultTypes.XML, null, ParamValue.PARAM_VALUE_SCALAR));
} else if (sequenceDataObject instanceof XmlSchemaAttribute) {
sequenceDataAttr = (XmlSchemaAttribute) sequenceDataObject;
defGroup.addElementEntry(new StaticOutputElement(dataService,
sequenceDataAttr.getName(), sequenceDataAttr
.getName(), sequenceDataAttr.getName(),
DBConstants.DBSFields.COLUMN,
DBConstants.DBSFields.ATTRIBUTE, namespace,
sequenceDataAttr.getSchemaTypeName(),
new HashSet<String>(), DBConstants.DataCategory.VALUE,
DBConstants.ResultTypes.XML, null, ParamValue.PARAM_VALUE_SCALAR));
} else {
throw new DataServiceFault(
"Xmlschema sequence at the out message's data field section of the operation '"
+ axisOperation.getName()
+ "' should only contain xml elements/attributes");
}
}
result.setDefaultElementGroup(defGroup);
return result;
}
private static boolean hasResultRowName(XmlSchemaComplexType wrapperSchemaComplexType) {
XmlSchemaParticle wrapperSchemaParticle = wrapperSchemaComplexType.getParticle();
// a single sequence must be there
if (!(wrapperSchemaParticle instanceof XmlSchemaSequence)) {
return false;
}
XmlSchemaSequence wrapperSchemaSequence = (XmlSchemaSequence) wrapperSchemaParticle;
XmlSchemaObjectCollection objects = wrapperSchemaSequence.getItems();
if (objects.getCount() != 1) {
return false;
}
XmlSchemaObject schemaObject = objects.getItem(0);
if (!(schemaObject instanceof XmlSchemaElement)) {
return false;
}
XmlSchemaElement schemaElement = (XmlSchemaElement) schemaObject;
if (!((((XmlSchemaComplexType) schemaElement.getSchemaType())
.getParticle()) instanceof XmlSchemaSequence)) {
return false;
}
// cannot contain any attributes
if (wrapperSchemaComplexType.getAttributes().getCount() > 0) {
return false;
}
return true;
}
private static String getResultRowName(XmlSchemaComplexType wrapperSchemaComplexType) {
return ((XmlSchemaElement) ((XmlSchemaSequence) wrapperSchemaComplexType
.getParticle()).getItems().getItem(0)).getName();
}
private static XmlSchemaComplexType getRowNameBasedSchemaComplexType(
XmlSchemaComplexType wrapperSchemaComplexType) {
return (((XmlSchemaComplexType) ((XmlSchemaElement) ((XmlSchemaSequence) wrapperSchemaComplexType
.getParticle()).getItems().getItem(0)).getSchemaType()));
}
private static XmlSchemaObjectCollection getSchemaObjectsFromComplexType(XmlSchemaComplexType schemaComplexType) {
XmlSchemaObjectCollection collection = new XmlSchemaObjectCollection();
XmlSchemaSequence sequence = (XmlSchemaSequence) schemaComplexType.getParticle();
if (sequence != null) {
XmlSchemaObjectCollection seqItems = sequence.getItems();
int c = seqItems.getCount();
for (int i = 0; i < c; i++) {
// add elements
collection.add(seqItems.getItem(i));
}
}
XmlSchemaObjectCollection attrItems = schemaComplexType.getAttributes();
int c = attrItems.getCount();
for (int i = 0; i < c; i++) {
// add attributes
collection.add(attrItems.getItem(i));
}
return collection;
}
private static ModelBean createModelBean(Map<QName, Document> modelMap, QName typeName) {
return createModelBean(modelMap, modelMap.get(typeName));
}
private static ModelBean createModelBean(Map<QName, Document> modelMap, Document doc) {
ModelBean bean = new ModelBean();
if (doc == null) {
return null;
}
Node beanEl = doc.getFirstChild();
/* populate bean attributes */
NamedNodeMap beanAttrs = beanEl.getAttributes();
bean.setName(beanAttrs.getNamedItem("originalName").getNodeValue());
bean.setNsURI(beanAttrs.getNamedItem("nsuri").getNodeValue());
Node isSimpleNode = beanAttrs.getNamedItem("simple");
if (isSimpleNode != null) {
bean.setSimple("yes".equals(isSimpleNode.getNodeValue()));
} else {
bean.setSimple(false);
}
/* populate child elements / properties */
NodeList propsElList = beanEl.getChildNodes();
int count = propsElList.getLength();
Node propEl;
for (int i = 0; i < count; i++) {
propEl = propsElList.item(i);
bean.addProperty(createModelProperty(modelMap, propEl));
}
return bean;
}
private static ModelProperty createModelProperty(Map<QName, Document> modelMap, Node propEl) {
ModelProperty property = new ModelProperty();
NamedNodeMap propAttrs = propEl.getAttributes();
property.setName(propAttrs.getNamedItem("name").getNodeValue());
Node isArrayNode = propAttrs.getNamedItem("array");
if (isArrayNode != null) {
property.setArray("yes".equals(isArrayNode.getNodeValue()));
} else {
property.setArray(false);
}
Node isPrimitiveNode = propAttrs.getNamedItem("primitive");
boolean primitive;
if (isPrimitiveNode != null) {
primitive = "yes".equals(isPrimitiveNode.getNodeValue());
} else {
primitive = false;
}
Node isSimpleNode = propAttrs.getNamedItem("simple");
boolean simple;
if (isSimpleNode != null) {
simple = "yes".equals(isSimpleNode.getNodeValue());
} else {
simple = false;
}
ModelBean type = null;
if (!primitive && !simple) {
type = createModelBean(modelMap,
new QName(propAttrs.getNamedItem("nsuri").getNodeValue(),
propAttrs.getNamedItem("shorttypename").getNodeValue()));
}
if (type != null) {
property.setType(type);
} else {
property.setSimpleType(propAttrs.getNamedItem("shorttypename").getNodeValue());
}
return property;
}
/**
* Represents a "Bean" element after schema compilation.
*/
public static class ModelBean {
private boolean simple;
private String nsURI;
/** type name */
private String name;
/** list of elements */
private List<ModelProperty> properties;
public ModelBean() {
this.properties = new ArrayList<ModelProperty>();
}
public boolean isSimple() {
return simple;
}
public void setSimple(boolean simple) {
this.simple = simple;
}
public String getNsURI() {
return nsURI;
}
public void setNsURI(String nsURI) {
this.nsURI = nsURI;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ModelProperty> getProperties() {
return properties;
}
public void setProperties(List<ModelProperty> properties) {
this.properties = properties;
}
public void addProperty(ModelProperty property) {
this.getProperties().add(property);
}
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("{\n");
buff.append("Name:" + this.getName() + "\n");
buff.append("NsURI:" + this.getNsURI() + "\n");
buff.append("Properties: {\n");
List<ModelProperty> propsList = this.getProperties();
int count = propsList.size();
for (int i = 0; i < count; i++) {
buff.append(propsList.get(i).toString());
if (i + 1 < count) {
buff.append(",\n");
}
}
buff.append("}\n");
buff.append("}\n");
return buff.toString();
}
}
/**
* Represents a "Property" element after schema compilation.
*/
public static class ModelProperty {
/** element name */
private String name;
private boolean array;
private String simpleType;
private ModelBean type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isArray() {
return array;
}
public void setArray(boolean array) {
this.array = array;
}
public String getSimpleType() {
return simpleType;
}
public void setSimpleType(String simpleType) {
this.simpleType = simpleType;
}
public ModelBean getType() {
return type;
}
public void setType(ModelBean type) {
this.type = type;
}
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("{\n");
buff.append("Name:" + this.getName() + "\n");
buff.append("IsArray:" + this.isArray() + "\n");
if (this.getType() != null) {
buff.append("Type:" + this.getType() + "\n");
} else {
buff.append("Type:" + this.getSimpleType());
}
buff.append("}\n");
return buff.toString();
}
}
}